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

//! Implements the traits found in [ecma402_traits::datetimeformat].

use ecma402_traits;
use rust_icu_common as common;
use rust_icu_udat as udat;
use rust_icu_uloc as uloc;
use rust_icu_ustring as ustring;
use std::{convert::TryFrom, fmt};

#[derive(Debug)]
pub struct DateTimeFormat {
    // The internal representation of date-time formatting.
    rep: udat::UDateFormat,
}

pub(crate) mod internal {
    use ecma402_traits::datetimeformat::DateTimeFormatOptions;
    use ecma402_traits::datetimeformat::options;
    use rust_icu_common as common;
    use rust_icu_ustring as ustring;
    use rust_icu_uloc as uloc;
    use std::convert::TryFrom;
    use anyhow::anyhow;

    fn check_auto_styling_set(opts: &DateTimeFormatOptions) -> Result<(), common::Error> {
        if opts.date_style.is_some() || opts.time_style.is_some() {
            return Err(common::Error::Wrapper(
                anyhow!("can't use custom styling because date_style or time_style is set")));
        }
        Ok(())
    }

    /// The skeleton pattern components come from:
    /// <https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetimepatterngenerator>
    pub fn skeleton_from_opts(opts: &DateTimeFormatOptions) -> Result<ustring::UChar, common::Error> {
        let mut skel = vec![];
        // Looks like date and time style are mutually exclusive with other
        // settings.
        if let Some(ref date_style) = &opts.date_style {
            match date_style {
                options::Style::Full => {}
                options::Style::Long => {}
                options::Style::Medium => {}
                options::Style::Short => {}
            }
        }
        if let Some(ref time_style) = &opts.time_style {
            match time_style {
                options::Style::Full => {}
                options::Style::Long => {}
                options::Style::Medium => {}
                options::Style::Short => {}
            }
        }
        if let Some(ref digits) = &opts.fractional_second_digits {
            check_auto_styling_set(opts)?;
            match digits.get() {
                1 => skel.push("S"),
                2 => skel.push("SS"),
                3 => skel.push("SSS"),
                _ => skel.push("SSSS"),
            }
        }
        if let Some(ref day_period) = &opts.day_period {
            check_auto_styling_set(opts)?;
            match day_period {
                options::DayPeriod::Short => skel.push("a"),
                // Not obvious for other match arms.
                options::DayPeriod::Long => {},
                options::DayPeriod::Narrow => {}
            }
        }
        if let Some(ref weekday) = &opts.weekday {
            check_auto_styling_set(opts)?;
            match weekday {
                options::Weekday::Long => skel.push("EEEE"),
                options::Weekday::Short => skel.push("EEE"),
                options::Weekday::Narrow => skel.push("EEEEEE"),
            }
        }
        if let Some(ref era) = &opts.era {
            check_auto_styling_set(opts)?;
            match era {
                options::Era::Long => skel.push("GGGG"),
                options::Era::Short => skel.push("GG"),
                options::Era::Narrow => skel.push("GGGGG"),
            }
        }
        if let Some(ref year) =&opts.year {
            check_auto_styling_set(opts)?;
            match year {
                options::DisplaySize::Numeric => skel.push("yyyy"),
                options::DisplaySize::TwoDigit => skel.push("yy"),
            }
        }
        if let Some(ref month) = &opts.month {
            check_auto_styling_set(opts)?;
            match month {
                options::Month::Numeric => skel.push("M"),
                options::Month::TwoDigit => skel.push("MM"),
                options::Month::Short => skel.push("MMM"),
                options::Month::Long => skel.push("MMMM"),
                options::Month::Narrow => skel.push("MMMMM"),
            }
        }
        if let Some(day) = &opts.day {
            check_auto_styling_set(opts)?;
            match day {
                options::DisplaySize::Numeric => skel.push("d"),
                options::DisplaySize::TwoDigit => skel.push("dd"),
            }
        }
        if let Some(hour) = &opts.hour {
            check_auto_styling_set(opts)?;
            if let Some(hour_cycle) = &opts.hour_cycle {
                match hour_cycle {
                    options::HourCycle::H11 =>
                        match hour {
                            options::DisplaySize::Numeric => skel.push("K"),
                            options::DisplaySize::TwoDigit => skel.push("KK"),
                    },
                    options::HourCycle::H12 =>
                        match hour {
                            options::DisplaySize::Numeric => skel.push("h"),
                            options::DisplaySize::TwoDigit => skel.push("hh"),
                        },
                    options::HourCycle::H23 =>
                        match hour {
                            options::DisplaySize::Numeric => skel.push("H"),
                            options::DisplaySize::TwoDigit => skel.push("HH"),
                        },
                    options::HourCycle::H24 => match hour {
                            options::DisplaySize::Numeric => skel.push("k"),
                            options::DisplaySize::TwoDigit => skel.push("kk"),
                        }
                }
            } else {
                // This may not be correct for 23/24 hours locale default.
                match hour {
                    options::DisplaySize::Numeric => skel.push("h"),
                    options::DisplaySize::TwoDigit => skel.push("hh"),
                }
            }
        }
        if let Some(minute) = &opts.minute {
            check_auto_styling_set(opts)?;
            match minute {
                options::DisplaySize::Numeric => skel.push("m"),
                options::DisplaySize::TwoDigit => skel.push("mm"),
            }
        }
        if let Some(second) = &opts.second {
            check_auto_styling_set(opts)?;
            match second {
                options::DisplaySize::Numeric => skel.push("s"),
                options::DisplaySize::TwoDigit => skel.push("ss"),
            }
        }
        if let Some(time_zone_style) = &opts.time_zone_style {
            check_auto_styling_set(opts)?;
            match time_zone_style {
                options::TimeZoneStyle::Long => skel.push("zzzz"),
                options::TimeZoneStyle::Short => skel.push("O"),
            }
        }
        let concat: String = skel.join(" ");
        ustring::UChar::try_from(&concat[..])
    }

    // Modifies the input locale based on the input pattern.  The settings here can only
    // be set on the locale used for formatting, not on the date time formatting pattern.
    //
    // Note: this only works with the BCP47 timezones ("uslax", not "America/Los_Angeles").
    pub fn locale_from_opts(locale: uloc::ULoc, opts: &DateTimeFormatOptions) -> uloc::ULoc {
        let mut locale = uloc::ULocMut::from(locale);

        if let Some(calendar) = &opts.calendar {
            locale.set_unicode_keyvalue("ca", &calendar.0);
        }
        if let Some(n) = &opts.numbering_system {
            locale.set_unicode_keyvalue("nu", &n.0);
        }
        if let Some(tz) = &opts.time_zone {
            locale.set_unicode_keyvalue("tz", &tz.0);
        }
        if let Some(h) = &opts.hour_cycle {
            locale.set_unicode_keyvalue("hc", &format!("{}", h));
        }
        // This will panic if any of the settings above are invalid.
        let loc = uloc::ULoc::from(locale);
        loc
    }
}

impl ecma402_traits::datetimeformat::DateTimeFormat for DateTimeFormat {
    type Error = common::Error;

    /// Creates a new [DateTimeFormat].
    ///
    /// Creation may fail, for example, if the locale-specific data is not loaded,
    /// or if the supplied options are inconsistent.
    fn try_new<L>(
        l: L,
        opts: ecma402_traits::datetimeformat::DateTimeFormatOptions,
    ) -> Result<Self, Self::Error>
    where
        L: ecma402_traits::Locale,
        Self: Sized,
    {
        let locale = internal::locale_from_opts(
            uloc::ULoc::try_from(&format!("{}", l)[..])?, &opts);
        let gen = udat::UDatePatternGenerator::new(&locale)?;

        let pattern = gen.get_best_pattern_ustring(
            &internal::skeleton_from_opts(&opts)?)?;

        // The correct timezone ID comes from the resulting locale.
        let tz_id = locale.keyword_value("timezone")?.or(Some("".to_owned())).unwrap();
        let tz_id = ustring::UChar::try_from(&tz_id[..])?;

        let rep = udat::UDateFormat::new_with_pattern(&locale, &tz_id, &pattern)?;
        Ok(DateTimeFormat { rep })
    }

    /// Formats `date` into the supplied `writer`.
    ///
    /// The function implements [`Intl.DateTimeFormat`][link1] from [ECMA 402][ecma].  The `date`
    /// is expressed in possibly fractional seconds since the Unix Epoch.  The formatting time zone
    /// and calendar are taken from the locale that was passed into `DateTimeFormat::try_new`.
    ///
    ///    [link1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
    ///    [ecma]: https://www.ecma-international.org/publications/standards/Ecma-402.htm
    fn format<W>(&self, date: f64, writer: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        let result = self.rep.format(date).map_err(|e| e.into())?;
        write!(writer, "{}", result)
    }
}

#[cfg(test)]
mod testing {
    use super::*;
    use ecma402_traits::datetimeformat::{options, DateTimeFormat, DateTimeFormatOptions, };
    use rust_icu_sys as usys;
    use rust_icu_uloc as uloc;
    use std::convert::TryFrom;

    #[test]
    fn date_time_format_examples() -> Result<(), common::Error> {
        #[derive(Debug, Clone)]
        struct TestCase {
            locale: &'static str,
            opts: DateTimeFormatOptions,
            dates: Vec<usys::UDate>,
            expected: Vec<&'static str>,
        }
        let tests = vec![
            TestCase {
                locale: "sr-RS",
                opts: DateTimeFormatOptions{
                    year:  Some(options::DisplaySize::Numeric),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["1970."],
            },
            // In ICU 63 this gets reported as "GMT-08:00", likely the ICU data
            // from then didn't contain the Serbian long spellout of the "uslax"
            // time zone.  Turning on from 67 onwards, since at the time this
            // test was written that was the oldest version that did have the
            // long spellout.
            #[cfg(feature="icu_version_67_plus")]
            TestCase {
                locale: "sr",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("uslax".to_owned())),
                    time_zone_style: Some(options::TimeZoneStyle::Long),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Северноамеричко пацифичко стандардно време"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("uslax".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Wednesday, Dec 31, 1969, 4:00:10 PM"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("uslax".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    time_zone_style: Some(options::TimeZoneStyle::Short),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Wednesday, Dec 31, 1969, 4:00:10 PM GMT-8"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("nlams".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    time_zone_style: Some(options::TimeZoneStyle::Short),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Thursday, Jan 1, 1970, 1:00:10 AM GMT+1"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("rumow".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    time_zone_style: Some(options::TimeZoneStyle::Short),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Thursday, Jan 1, 1970, 3:00:10 AM GMT+3"],
            },
        ];
        for test in tests {
            let locale =
                crate::Locale::FromULoc(uloc::ULoc::try_from(test.locale).expect("locale exists"));
            let formatter = super::DateTimeFormat::try_new(locale, test.clone().opts)?;
            let actual = test
                .dates
                .iter()
                .map(|d| {
                    let mut result = String::new();
                    formatter
                        .format(*d, &mut result)
                        .expect(&format!("can format: {}", d));
                    result
                })
                .collect::<Vec<String>>();
            assert_eq!(test.expected, actual, "for test case: {:?}", &test);
        }
        Ok(())
    }
}
<commit_msg>Deflake the date time format test<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Implements the traits found in [ecma402_traits::datetimeformat].

use ecma402_traits;
use rust_icu_common as common;
use rust_icu_udat as udat;
use rust_icu_uloc as uloc;
use rust_icu_ustring as ustring;
use std::{convert::TryFrom, fmt};

#[derive(Debug)]
pub struct DateTimeFormat {
    // The internal representation of date-time formatting.
    rep: udat::UDateFormat,
}

pub(crate) mod internal {
    use ecma402_traits::datetimeformat::DateTimeFormatOptions;
    use ecma402_traits::datetimeformat::options;
    use rust_icu_common as common;
    use rust_icu_ustring as ustring;
    use rust_icu_uloc as uloc;
    use std::convert::TryFrom;
    use anyhow::anyhow;

    fn check_auto_styling_set(opts: &DateTimeFormatOptions) -> Result<(), common::Error> {
        if opts.date_style.is_some() || opts.time_style.is_some() {
            return Err(common::Error::Wrapper(
                anyhow!("can't use custom styling because date_style or time_style is set")));
        }
        Ok(())
    }

    /// The skeleton pattern components come from:
    /// <https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetimepatterngenerator>
    pub fn skeleton_from_opts(opts: &DateTimeFormatOptions) -> Result<ustring::UChar, common::Error> {
        let mut skel = vec![];
        // Looks like date and time style are mutually exclusive with other
        // settings.
        if let Some(ref date_style) = &opts.date_style {
            match date_style {
                options::Style::Full => {}
                options::Style::Long => {}
                options::Style::Medium => {}
                options::Style::Short => {}
            }
        }
        if let Some(ref time_style) = &opts.time_style {
            match time_style {
                options::Style::Full => {}
                options::Style::Long => {}
                options::Style::Medium => {}
                options::Style::Short => {}
            }
        }
        if let Some(ref digits) = &opts.fractional_second_digits {
            check_auto_styling_set(opts)?;
            match digits.get() {
                1 => skel.push("S"),
                2 => skel.push("SS"),
                3 => skel.push("SSS"),
                _ => skel.push("SSSS"),
            }
        }
        if let Some(ref day_period) = &opts.day_period {
            check_auto_styling_set(opts)?;
            match day_period {
                options::DayPeriod::Short => skel.push("a"),
                // Not obvious for other match arms.
                options::DayPeriod::Long => {},
                options::DayPeriod::Narrow => {}
            }
        }
        if let Some(ref weekday) = &opts.weekday {
            check_auto_styling_set(opts)?;
            match weekday {
                options::Weekday::Long => skel.push("EEEE"),
                options::Weekday::Short => skel.push("EEE"),
                options::Weekday::Narrow => skel.push("EEEEEE"),
            }
        }
        if let Some(ref era) = &opts.era {
            check_auto_styling_set(opts)?;
            match era {
                options::Era::Long => skel.push("GGGG"),
                options::Era::Short => skel.push("GG"),
                options::Era::Narrow => skel.push("GGGGG"),
            }
        }
        if let Some(ref year) =&opts.year {
            check_auto_styling_set(opts)?;
            match year {
                options::DisplaySize::Numeric => skel.push("yyyy"),
                options::DisplaySize::TwoDigit => skel.push("yy"),
            }
        }
        if let Some(ref month) = &opts.month {
            check_auto_styling_set(opts)?;
            match month {
                options::Month::Numeric => skel.push("M"),
                options::Month::TwoDigit => skel.push("MM"),
                options::Month::Short => skel.push("MMM"),
                options::Month::Long => skel.push("MMMM"),
                options::Month::Narrow => skel.push("MMMMM"),
            }
        }
        if let Some(day) = &opts.day {
            check_auto_styling_set(opts)?;
            match day {
                options::DisplaySize::Numeric => skel.push("d"),
                options::DisplaySize::TwoDigit => skel.push("dd"),
            }
        }
        if let Some(hour) = &opts.hour {
            check_auto_styling_set(opts)?;
            if let Some(hour_cycle) = &opts.hour_cycle {
                match hour_cycle {
                    options::HourCycle::H11 =>
                        match hour {
                            options::DisplaySize::Numeric => skel.push("K"),
                            options::DisplaySize::TwoDigit => skel.push("KK"),
                    },
                    options::HourCycle::H12 =>
                        match hour {
                            options::DisplaySize::Numeric => skel.push("h"),
                            options::DisplaySize::TwoDigit => skel.push("hh"),
                        },
                    options::HourCycle::H23 =>
                        match hour {
                            options::DisplaySize::Numeric => skel.push("H"),
                            options::DisplaySize::TwoDigit => skel.push("HH"),
                        },
                    options::HourCycle::H24 => match hour {
                            options::DisplaySize::Numeric => skel.push("k"),
                            options::DisplaySize::TwoDigit => skel.push("kk"),
                        }
                }
            } else {
                // This may not be correct for 23/24 hours locale default.
                match hour {
                    options::DisplaySize::Numeric => skel.push("h"),
                    options::DisplaySize::TwoDigit => skel.push("hh"),
                }
            }
        }
        if let Some(minute) = &opts.minute {
            check_auto_styling_set(opts)?;
            match minute {
                options::DisplaySize::Numeric => skel.push("m"),
                options::DisplaySize::TwoDigit => skel.push("mm"),
            }
        }
        if let Some(second) = &opts.second {
            check_auto_styling_set(opts)?;
            match second {
                options::DisplaySize::Numeric => skel.push("s"),
                options::DisplaySize::TwoDigit => skel.push("ss"),
            }
        }
        if let Some(time_zone_style) = &opts.time_zone_style {
            check_auto_styling_set(opts)?;
            match time_zone_style {
                options::TimeZoneStyle::Long => skel.push("zzzz"),
                options::TimeZoneStyle::Short => skel.push("O"),
            }
        }
        let concat: String = skel.join(" ");
        ustring::UChar::try_from(&concat[..])
    }

    // Modifies the input locale based on the input pattern.  The settings here can only
    // be set on the locale used for formatting, not on the date time formatting pattern.
    //
    // Note: this only works with the BCP47 timezones ("uslax", not "America/Los_Angeles").
    pub fn locale_from_opts(locale: uloc::ULoc, opts: &DateTimeFormatOptions) -> uloc::ULoc {
        let mut locale = uloc::ULocMut::from(locale);

        if let Some(calendar) = &opts.calendar {
            locale.set_unicode_keyvalue("ca", &calendar.0);
        }
        if let Some(n) = &opts.numbering_system {
            locale.set_unicode_keyvalue("nu", &n.0);
        }
        if let Some(tz) = &opts.time_zone {
            locale.set_unicode_keyvalue("tz", &tz.0);
        }
        if let Some(h) = &opts.hour_cycle {
            locale.set_unicode_keyvalue("hc", &format!("{}", h));
        }
        // This will panic if any of the settings above are invalid.
        let loc = uloc::ULoc::from(locale);
        loc
    }
}

impl ecma402_traits::datetimeformat::DateTimeFormat for DateTimeFormat {
    type Error = common::Error;

    /// Creates a new [DateTimeFormat].
    ///
    /// Creation may fail, for example, if the locale-specific data is not loaded,
    /// or if the supplied options are inconsistent.
    fn try_new<L>(
        l: L,
        opts: ecma402_traits::datetimeformat::DateTimeFormatOptions,
    ) -> Result<Self, Self::Error>
    where
        L: ecma402_traits::Locale,
        Self: Sized,
    {
        let locale = internal::locale_from_opts(
            uloc::ULoc::try_from(&format!("{}", l)[..])?, &opts);
        let gen = udat::UDatePatternGenerator::new(&locale)?;

        let pattern = gen.get_best_pattern_ustring(
            &internal::skeleton_from_opts(&opts)?)?;

        // The correct timezone ID comes from the resulting locale.
        let tz_id = locale.keyword_value("timezone")?.or(Some("".to_owned())).unwrap();
        let tz_id = ustring::UChar::try_from(&tz_id[..])?;

        let rep = udat::UDateFormat::new_with_pattern(&locale, &tz_id, &pattern)?;
        Ok(DateTimeFormat { rep })
    }

    /// Formats `date` into the supplied `writer`.
    ///
    /// The function implements [`Intl.DateTimeFormat`][link1] from [ECMA 402][ecma].  The `date`
    /// is expressed in possibly fractional seconds since the Unix Epoch.  The formatting time zone
    /// and calendar are taken from the locale that was passed into `DateTimeFormat::try_new`.
    ///
    ///    [link1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat
    ///    [ecma]: https://www.ecma-international.org/publications/standards/Ecma-402.htm
    fn format<W>(&self, date: f64, writer: &mut W) -> fmt::Result
    where
        W: fmt::Write,
    {
        let result = self.rep.format(date).map_err(|e| e.into())?;
        write!(writer, "{}", result)
    }
}

#[cfg(test)]
mod testing {
    use super::*;
    use ecma402_traits::datetimeformat::{options, DateTimeFormat, DateTimeFormatOptions, };
    use rust_icu_sys as usys;
    use rust_icu_uloc as uloc;
    use std::convert::TryFrom;

    #[test]
    fn date_time_format_examples() -> Result<(), common::Error> {
        #[derive(Debug, Clone)]
        struct TestCase {
            locale: &'static str,
            opts: DateTimeFormatOptions,
            dates: Vec<usys::UDate>,
            expected: Vec<&'static str>,
        }
        let tests = vec![
            TestCase {
                locale: "sr-RS",
                opts: DateTimeFormatOptions{
                    year:  Some(options::DisplaySize::Numeric),
                    // If time_zone is unset, this becomes a conversion into the
                    // "local" time zone, which may make this test brittle.
                    time_zone: Some("UTC".into()),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["1970."],
            },
            // In ICU 63 this gets reported as "GMT-08:00", likely the ICU data
            // from then didn't contain the Serbian long spellout of the "uslax"
            // time zone.  Turning on from 67 onwards, since at the time this
            // test was written that was the oldest version that did have the
            // long spellout.
            #[cfg(feature="icu_version_67_plus")]
            TestCase {
                locale: "sr",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("uslax".to_owned())),
                    time_zone_style: Some(options::TimeZoneStyle::Long),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Северноамеричко пацифичко стандардно време"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("uslax".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Wednesday, Dec 31, 1969, 4:00:10 PM"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("uslax".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    time_zone_style: Some(options::TimeZoneStyle::Short),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Wednesday, Dec 31, 1969, 4:00:10 PM GMT-8"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("nlams".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    time_zone_style: Some(options::TimeZoneStyle::Short),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Thursday, Jan 1, 1970, 1:00:10 AM GMT+1"],
            },
            TestCase {
                locale: "en-US",
                opts: DateTimeFormatOptions{
                    time_zone: Some(options::TimeZone("rumow".to_owned())),
                    weekday: Some(options::Weekday::Long),
                    year:  Some(options::DisplaySize::Numeric),
                    day: Some(options::DisplaySize::Numeric,),
                    month: Some(options::Month::Short),
                    hour: Some(options::DisplaySize::Numeric),
                    minute: Some(options::DisplaySize::Numeric),
                    second: Some(options::DisplaySize::Numeric),
                    time_zone_style: Some(options::TimeZoneStyle::Short),
                    ..Default::default()
                },

                dates: vec![10000_f64],
                expected: vec!["Thursday, Jan 1, 1970, 3:00:10 AM GMT+3"],
            },
        ];
        for test in tests {
            let locale =
                crate::Locale::FromULoc(uloc::ULoc::try_from(test.locale).expect("locale exists"));
            let formatter = super::DateTimeFormat::try_new(locale, test.clone().opts)?;
            let actual = test
                .dates
                .iter()
                .map(|d| {
                    let mut result = String::new();
                    formatter
                        .format(*d, &mut result)
                        .expect(&format!("can format: {}", d));
                    result
                })
                .collect::<Vec<String>>();
            assert_eq!(test.expected, actual, "for test case: {:?}", &test);
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::too_many_arguments)]

use std::cell::RefCell;

use slog::Logger;
use smithay::{
    backend::{
        renderer::{Frame, Renderer, Texture, Transform, ImportShm, ImportDma, BufferType, buffer_type},
        SwapBuffersError,
    },
    reexports::wayland_server::protocol::{wl_buffer, wl_surface},
    utils::Rectangle,
    wayland::{
        compositor::{roles::Role, Damage, SubsurfaceRole, TraversalAction},
        data_device::DnDIconRole,
        seat::CursorImageRole,
    },
};
#[cfg(feature = "egl")]
use smithay::backend::{
    egl::display::EGLBufferReader,
    renderer::ImportEgl,
};
// hacky...
#[cfg(not(feature = "egl"))]
pub trait ImportEgl {}
#[cfg(not(feature = "egl"))]
impl<T> ImportEgl for T {}

use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};

struct BufferTextures<T> {
    buffer: Option<wl_buffer::WlBuffer>,
    texture: T,
}

impl<T> Drop for BufferTextures<T> {
    fn drop(&mut self) {
        if let Some(buffer) = self.buffer.take() {
            buffer.release();
        }
    }
}

pub fn draw_cursor<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    surface: &wl_surface::WlSurface,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    (x, y): (i32, i32),
    token: MyCompositorToken,
    log: &Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
        Ok(h) => h,
        Err(_) => {
            warn!(
                log,
                "Trying to display as a cursor a surface that does not have the CursorImage role."
            );
            (0, 0)
        }
    };
    draw_surface_tree(
        renderer,
        frame,
        surface,
        #[cfg(feature = "egl")]
        egl_buffer_reader,
        (x - dx, y - dy),
        token,
        log,
    )
}

fn draw_surface_tree<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    root: &wl_surface::WlSurface,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    location: (i32, i32),
    compositor_token: MyCompositorToken,
    log: &Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    let mut result = Ok(());

    compositor_token.with_surface_tree_upward(
        root,
        location,
        |_surface, attributes, role, &(mut x, mut y)| {
            // Pull a new buffer if available
            if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
                let mut data = data.borrow_mut();
                if data.texture.is_none() {
                    if let Some(buffer) = data.current_state.buffer.take() {
                        let texture = match buffer_type(
                            &buffer,
                            #[cfg(feature = "egl")]
                            egl_buffer_reader
                        ) {
                            Some(BufferType::Shm) => {
                                let damage = attributes
                                    .damage
                                    .iter()
                                    .map(|dmg| match dmg {
                                        Damage::Buffer(rect) => *rect,
                                        // TODO also apply transformations
                                        Damage::Surface(rect) => rect.scale(attributes.buffer_scale),
                                    })
                                    .collect::<Vec<_>>();
                                let result = renderer.import_shm_buffer(&buffer, Some(&attributes), &damage);
                                if result.is_ok() {
                                    buffer.release();
                                }
                                Some(result)
                            },
                            #[cfg(feature = "egl")]
                            Some(BufferType::Egl) => Some(renderer.import_egl_buffer(&buffer, egl_buffer_reader.unwrap())),
                            Some(BufferType::Dma) => Some(renderer.import_dma_buffer(&buffer)),
                            _ => {
                                error!(log, "Unknown buffer format for: {:?}", buffer);
                                None
                            }
                        };
                        match texture {
                            Some(Ok(m)) => {
                                data.texture = Some(Box::new(BufferTextures { buffer: Some(buffer), texture: m })
                                    as Box<dyn std::any::Any + 'static>)
                            }
                            // there was an error reading the buffer, release it.
                            Some(Err(err)) => {
                                warn!(log, "Error loading buffer: {:?}", err);
                                buffer.release();
                            },
                            None => buffer.release(),
                        };
                    }
                }
                // Now, should we be drawn ?
                if data.texture.is_some() {
                    // if yes, also process the children
                    if Role::<SubsurfaceRole>::has(role) {
                        x += data.current_state.sub_location.0;
                        y += data.current_state.sub_location.1;
                    }
                    TraversalAction::DoChildren((x, y))
                } else {
                    // we are not displayed, so our children are neither
                    TraversalAction::SkipChildren
                }
            } else {
                // we are not displayed, so our children are neither
                TraversalAction::SkipChildren
            }
        },
        |_surface, attributes, role, &(mut x, mut y)| {
            if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
                let mut data = data.borrow_mut();
                let (sub_x, sub_y) = data.current_state.sub_location;
                if let Some(texture) = data
                    .texture
                    .as_mut()
                    .and_then(|x| x.downcast_mut::<BufferTextures<T>>())
                {
                    // we need to re-extract the subsurface offset, as the previous closure
                    // only passes it to our children
                    if Role::<SubsurfaceRole>::has(role) {
                        x += sub_x;
                        y += sub_y;
                    }
                    if let Err(err) = frame.render_texture_at(
                        &texture.texture,
                        (x, y),
                        Transform::Normal, /* TODO */
                        1.0,
                    ) {
                        result = Err(err.into());
                    }
                }
            }
        },
        |_, _, _, _| true,
    );

    result
}

pub fn draw_windows<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    window_map: &MyWindowMap,
    output_rect: Option<Rectangle>,
    compositor_token: MyCompositorToken,
    log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    let mut result = Ok(());

    // redraw the frame, in a simple but inneficient way
    window_map.with_windows_from_bottom_to_top(|toplevel_surface, mut initial_place, bounding_box| {
        // skip windows that do not overlap with a given output
        if let Some(output) = output_rect {
            if !output.overlaps(bounding_box) {
                return;
            }
            initial_place.0 -= output.x;
        }
        if let Some(wl_surface) = toplevel_surface.get_surface() {
            // this surface is a root of a subsurface tree that needs to be drawn
            if let Err(err) = draw_surface_tree(
                renderer,
                frame,
                &wl_surface,
                #[cfg(feature = "egl")]
                egl_buffer_reader,
                initial_place,
                compositor_token,
                log,
            ) {
                result = Err(err);
            }
        }
    });

    result
}

pub fn draw_dnd_icon<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    surface: &wl_surface::WlSurface,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    (x, y): (i32, i32),
    token: MyCompositorToken,
    log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    if !token.has_role::<DnDIconRole>(surface) {
        warn!(
            log,
            "Trying to display as a dnd icon a surface that does not have the DndIcon role."
        );
    }
    draw_surface_tree(
        renderer,
        frame,
        surface,
        #[cfg(feature = "egl")]
        egl_buffer_reader,
        (x, y),
        token,
        log
    )
}
<commit_msg>anvil: don't release SHM buffers twice<commit_after>#![allow(clippy::too_many_arguments)]

use std::cell::RefCell;

use slog::Logger;
use smithay::{
    backend::{
        renderer::{Frame, Renderer, Texture, Transform, ImportShm, ImportDma, BufferType, buffer_type},
        SwapBuffersError,
    },
    reexports::wayland_server::protocol::{wl_buffer, wl_surface},
    utils::Rectangle,
    wayland::{
        compositor::{roles::Role, Damage, SubsurfaceRole, TraversalAction},
        data_device::DnDIconRole,
        seat::CursorImageRole,
    },
};
#[cfg(feature = "egl")]
use smithay::backend::{
    egl::display::EGLBufferReader,
    renderer::ImportEgl,
};
// hacky...
#[cfg(not(feature = "egl"))]
pub trait ImportEgl {}
#[cfg(not(feature = "egl"))]
impl<T> ImportEgl for T {}

use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};

struct BufferTextures<T> {
    buffer: Option<wl_buffer::WlBuffer>,
    texture: T,
}

impl<T> Drop for BufferTextures<T> {
    fn drop(&mut self) {
        if let Some(buffer) = self.buffer.take() {
            buffer.release();
        }
    }
}

pub fn draw_cursor<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    surface: &wl_surface::WlSurface,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    (x, y): (i32, i32),
    token: MyCompositorToken,
    log: &Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
        Ok(h) => h,
        Err(_) => {
            warn!(
                log,
                "Trying to display as a cursor a surface that does not have the CursorImage role."
            );
            (0, 0)
        }
    };
    draw_surface_tree(
        renderer,
        frame,
        surface,
        #[cfg(feature = "egl")]
        egl_buffer_reader,
        (x - dx, y - dy),
        token,
        log,
    )
}

fn draw_surface_tree<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    root: &wl_surface::WlSurface,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    location: (i32, i32),
    compositor_token: MyCompositorToken,
    log: &Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    let mut result = Ok(());

    compositor_token.with_surface_tree_upward(
        root,
        location,
        |_surface, attributes, role, &(mut x, mut y)| {
            // Pull a new buffer if available
            if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
                let mut data = data.borrow_mut();
                if data.texture.is_none() {
                    if let Some(buffer) = data.current_state.buffer.take() {
                        let texture = match buffer_type(
                            &buffer,
                            #[cfg(feature = "egl")]
                            egl_buffer_reader
                        ) {
                            Some(BufferType::Shm) => {
                                let damage = attributes
                                    .damage
                                    .iter()
                                    .map(|dmg| match dmg {
                                        Damage::Buffer(rect) => *rect,
                                        // TODO also apply transformations
                                        Damage::Surface(rect) => rect.scale(attributes.buffer_scale),
                                    })
                                    .collect::<Vec<_>>();
                                let result = renderer.import_shm_buffer(&buffer, Some(&attributes), &damage);
                                buffer.release();
                                // don't return the buffer as it is already released
                                Some((result, None))
                            },
                            #[cfg(feature = "egl")]
                            Some(BufferType::Egl) => Some((renderer.import_egl_buffer(&buffer, egl_buffer_reader.unwrap()), Some(buffer))),
                            Some(BufferType::Dma) => Some((renderer.import_dma_buffer(&buffer), Some(buffer))),
                            _ => {
                                error!(log, "Unknown buffer format for: {:?}", buffer);
                                buffer.release();
                                None
                            }
                        };
                        match texture {
                            Some((Ok(m), buffer)) => {
                                data.texture = Some(Box::new(BufferTextures { buffer, texture: m })
                                    as Box<dyn std::any::Any + 'static>)
                            }
                            // there was an error reading the buffer, release it.
                            Some((Err(err), buffer)) => {
                                warn!(log, "Error loading buffer: {:?}", err);
                                if let Some(buffer) = buffer {
                                    buffer.release();
                                }
                            },
                            None => {},
                        };
                    }
                }
                // Now, should we be drawn ?
                if data.texture.is_some() {
                    // if yes, also process the children
                    if Role::<SubsurfaceRole>::has(role) {
                        x += data.current_state.sub_location.0;
                        y += data.current_state.sub_location.1;
                    }
                    TraversalAction::DoChildren((x, y))
                } else {
                    // we are not displayed, so our children are neither
                    TraversalAction::SkipChildren
                }
            } else {
                // we are not displayed, so our children are neither
                TraversalAction::SkipChildren
            }
        },
        |_surface, attributes, role, &(mut x, mut y)| {
            if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
                let mut data = data.borrow_mut();
                let (sub_x, sub_y) = data.current_state.sub_location;
                if let Some(texture) = data
                    .texture
                    .as_mut()
                    .and_then(|x| x.downcast_mut::<BufferTextures<T>>())
                {
                    // we need to re-extract the subsurface offset, as the previous closure
                    // only passes it to our children
                    if Role::<SubsurfaceRole>::has(role) {
                        x += sub_x;
                        y += sub_y;
                    }
                    if let Err(err) = frame.render_texture_at(
                        &texture.texture,
                        (x, y),
                        Transform::Normal, /* TODO */
                        1.0,
                    ) {
                        result = Err(err.into());
                    }
                }
            }
        },
        |_, _, _, _| true,
    );

    result
}

pub fn draw_windows<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    window_map: &MyWindowMap,
    output_rect: Option<Rectangle>,
    compositor_token: MyCompositorToken,
    log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    let mut result = Ok(());

    // redraw the frame, in a simple but inneficient way
    window_map.with_windows_from_bottom_to_top(|toplevel_surface, mut initial_place, bounding_box| {
        // skip windows that do not overlap with a given output
        if let Some(output) = output_rect {
            if !output.overlaps(bounding_box) {
                return;
            }
            initial_place.0 -= output.x;
        }
        if let Some(wl_surface) = toplevel_surface.get_surface() {
            // this surface is a root of a subsurface tree that needs to be drawn
            if let Err(err) = draw_surface_tree(
                renderer,
                frame,
                &wl_surface,
                #[cfg(feature = "egl")]
                egl_buffer_reader,
                initial_place,
                compositor_token,
                log,
            ) {
                result = Err(err);
            }
        }
    });

    result
}

pub fn draw_dnd_icon<R, E, F, T>(
    renderer: &mut R,
    frame: &mut F,
    surface: &wl_surface::WlSurface,
    #[cfg(feature = "egl")]
    egl_buffer_reader: Option<&EGLBufferReader>,
    (x, y): (i32, i32),
    token: MyCompositorToken,
    log: &::slog::Logger,
) -> Result<(), SwapBuffersError>
where
    R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma,
    F: Frame<Error = E, TextureId = T>,
    E: std::error::Error + Into<SwapBuffersError>,
    T: Texture + 'static,
{
    if !token.has_role::<DnDIconRole>(surface) {
        warn!(
            log,
            "Trying to display as a dnd icon a surface that does not have the DndIcon role."
        );
    }
    draw_surface_tree(
        renderer,
        frame,
        surface,
        #[cfg(feature = "egl")]
        egl_buffer_reader,
        (x, y),
        token,
        log
    )
}
<|endoftext|>


<commit_before>use std::io::{Read, BufRead, Error as IoError};
use std::str::{from_utf8, Utf8Error};

use num::bigint::{BigInt, ParseBigIntError};
use byteorder::{ReadBytesExt, LittleEndian, Error as ByteorderError};

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        ReadError(err: ByteorderError) {
            from()
        }
        IoError(err: IoError) {
            from()
        }
        UnknownOpcode(opcode: u8) {}
        InvalidInt {
            from(Utf8Error)
            from(ParseBigIntError)
        }
        InvalidString
        ExpectedTrailingL
    }
}

#[derive(Debug)]
pub enum OpCode {
    Int(BigInt),
    BinInt(i32),
    BinInt1(u8),
    BinInt2(u16),
    Long(BigInt),
    Long1(u8),
    Long4(i32),

    String(Vec<u8>),
    BinString(Vec<u8>),
    ShortBinString(Vec<u8>),

    None,

    NewTrue,
    NewFalse,

    Unicode(String),
    BinUnicode(String),

    Float(f64),
    BinFloat(f64),

    EmptyList,
    Append,
    Appends,
    List,

    EmptyTuple,
    Tuple,
    Tuple1,
    Tuple2,
    Tuple3,

    EmptyDict,
    Dict,
    SetItem,
    SetItems,

    Pop,
    Dup,
    Mark,
    PopMark,

    Get(usize),
    BinGet(usize),
    LongBinGet(usize),
    Put(usize),
    BinPut(usize),
    LongBinPut(usize),

    Ext1(u8),
    Ext2(u16),
    Ext4(i32),

    Global(Vec<u8>, Vec<u8>),
    Reduce,
    Build,
    Inst(Vec<u8>, Vec<u8>),
    Obj,
    NewObj,
    Proto(u8),
    Stop,
    PersId(Vec<u8>),
    BinPersId,
}

pub fn read_until_newline<R>(rd: &mut R) -> Result<Vec<u8>, Error> where R: Read + BufRead {
    let mut buf = Vec::new();
    try!(rd.read_until('\n' as u8, &mut buf));

    // Skip last symbol — \n
    match buf.split_last() {
        None => Err(Error::InvalidString),
        Some((_last, init)) => Ok(init.to_vec()),
    }
}

pub fn read_opcode<R>(rd: &mut R) -> Result<OpCode, Error> where R: Read + BufRead {
    let marker = try!(rd.read_u8());
    return Ok(match marker {
        73 => {
            let s = try!(read_until_newline(rd));
            OpCode::Int(try!(try!(from_utf8(&s)).parse()))
        },
        74 => OpCode::BinInt(try!(rd.read_i32::<LittleEndian>())),
        75 => OpCode::BinInt1(try!(rd.read_u8())),
        77 => OpCode::BinInt2(try!(rd.read_u16::<LittleEndian>())),
        76 => {
            let s = try!(read_until_newline(rd));

            let init = match s.split_last() {
                None => return Err(Error::InvalidString),
                Some((&76, init)) => init,
                Some(_) => return Err(Error::ExpectedTrailingL),
            };

            OpCode::Long(try!(try!(from_utf8(init)).parse()))
        },
        138 => OpCode::Long1(try!(rd.read_u8())),
        139 => OpCode::Long4(try!(rd.read_i32::<LittleEndian>())),
        c => return Err(Error::UnknownOpcode(c)),
    })
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor};

    use num::{FromPrimitive};

    use super::{OpCode, Error, read_opcode};

    macro_rules! t {
        ($buffer: expr, $pat:pat, $result:expr) => ({
            match read_opcode(&mut Cursor::new(&$buffer[..])) {
                $pat => $result,
                other => {
                    println!("ERROR {:?}", other);
                    assert!(false)
                },
            }
        })
    }

    #[test]
    fn test_int() {
        t!(b"I", Err(Error::InvalidString), assert!(true));
        t!(b"I\n", Err(Error::InvalidInt), assert!(true));
        t!(b"Iabc\n", Err(Error::InvalidInt), assert!(true));
        t!(b"I123\n", Ok(OpCode::Int(n)), assert_eq!(n, FromPrimitive::from_usize(123).unwrap()));
    }

    #[test]
    fn test_bin_int() {
        t!(b"J\x0a", Err(Error::ReadError(_)), assert!(true));
        t!(b"J\x0a\x00\x00\x00", Ok(OpCode::BinInt(n)), assert_eq!(n, 10));
        t!(b"J\x0a\x00\x00\x01", Ok(OpCode::BinInt(n)), assert_eq!(n, 16777226));
    }

    #[test]
    fn test_bin_int1() {
        t!(b"K", Err(Error::ReadError(_)), assert!(true));
        t!(b"K\x0a", Ok(OpCode::BinInt1(n)), assert_eq!(n, 10));
    }

    #[test]
    fn test_bin_int2() {
        t!(b"M\x0a", Err(Error::ReadError(_)), assert!(true));
        t!(b"M\x0a\x00\x00\x00", Ok(OpCode::BinInt2(n)), assert_eq!(n, 10));
        t!(b"M\x0a\x01\x00\x00", Ok(OpCode::BinInt2(n)), assert_eq!(n, 266));
    }

    #[test]
    fn test_long() {
        t!(b"L", Err(Error::InvalidString), assert!(true));
        t!(b"L\n", Err(Error::InvalidString), assert!(true));
        t!(b"Labc\n", Err(Error::ExpectedTrailingL), assert!(true));
        t!(b"LabcL\n", Err(Error::InvalidInt), assert!(true));
        t!(b"L123L\n", Ok(OpCode::Long(n)), assert_eq!(n, FromPrimitive::from_usize(123).unwrap()));
    }

    #[test]
    fn test_long1() {
        t!(b"\x8a", Err(Error::ReadError(_)), assert!(true));
        t!(b"\x8a\x0a", Ok(OpCode::Long1(n)), assert_eq!(n, 10));
    }

    #[test]
    fn test_long4() {
        t!(b"\x8b\x0a", Err(Error::ReadError(_)), assert!(true));
        t!(b"\x8b\x0a\x00\x00\x00", Ok(OpCode::Long4(n)), assert_eq!(n, 10));
        t!(b"\x8b\x0a\x00\x00\x01", Ok(OpCode::Long4(n)), assert_eq!(n, 16777226));
    }
}
<commit_msg>Fix long decoding<commit_after>use std::io::{Read, BufRead, Error as IoError};
use std::str::{from_utf8, Utf8Error};

use num::bigint::{BigInt, ToBigInt, Sign, ParseBigIntError};
use byteorder::{ReadBytesExt, LittleEndian, Error as ByteorderError};

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        ReadError(err: ByteorderError) {
            from()
        }
        IoError(err: IoError) {
            from()
        }
        UnknownOpcode(opcode: u8) {}
        InvalidInt {
            from(Utf8Error)
            from(ParseBigIntError)
        }
        InvalidString
        ExpectedTrailingL
        InvalidLong
    }
}

#[derive(Debug)]
pub enum OpCode {
    Int(BigInt),
    BinInt(i32),
    BinInt1(u8),
    BinInt2(u16),
    Long(BigInt),
    Long1(BigInt),
    Long4(BigInt),

    String(Vec<u8>),
    BinString(Vec<u8>),
    ShortBinString(Vec<u8>),

    None,

    NewTrue,
    NewFalse,

    Unicode(String),
    BinUnicode(String),

    Float(f64),
    BinFloat(f64),

    EmptyList,
    Append,
    Appends,
    List,

    EmptyTuple,
    Tuple,
    Tuple1,
    Tuple2,
    Tuple3,

    EmptyDict,
    Dict,
    SetItem,
    SetItems,

    Pop,
    Dup,
    Mark,
    PopMark,

    Get(usize),
    BinGet(usize),
    LongBinGet(usize),
    Put(usize),
    BinPut(usize),
    LongBinPut(usize),

    Ext1(u8),
    Ext2(u16),
    Ext4(i32),

    Global(Vec<u8>, Vec<u8>),
    Reduce,
    Build,
    Inst(Vec<u8>, Vec<u8>),
    Obj,
    NewObj,
    Proto(u8),
    Stop,
    PersId(Vec<u8>),
    BinPersId,
}

pub fn read_until_newline<R>(rd: &mut R) -> Result<Vec<u8>, Error> where R: Read + BufRead {
    let mut buf = Vec::new();
    try!(rd.read_until('\n' as u8, &mut buf));

    // Skip last symbol — \n
    match buf.split_last() {
        None => Err(Error::InvalidString),
        Some((_last, init)) => Ok(init.to_vec()),
    }
}

pub fn read_long<R>(rd: &mut R, length: usize) -> Result<BigInt, Error> where R: Read + BufRead {
    let mut buf = vec![0; length];
    try!(rd.read_exact(buf.as_mut()));

    let mut n = BigInt::from_bytes_le(Sign::Plus, &buf);

    let last = match buf.last_mut() {
        None => return Err(Error::InvalidLong),
        Some(last) => last,
    };

    if *last > 127 {
        n = n - (1.to_bigint().unwrap() << (length * 8))
    }

    Ok(n)
}

pub fn read_opcode<R>(rd: &mut R) -> Result<OpCode, Error> where R: Read + BufRead {
    let marker = try!(rd.read_u8());
    return Ok(match marker {
        73 => {
            let s = try!(read_until_newline(rd));
            OpCode::Int(try!(try!(from_utf8(&s)).parse()))  // http://rust-num.github.io/num/num/bigint/struct.BigInt.html#method.parse_bytes
        },
        74 => OpCode::BinInt(try!(rd.read_i32::<LittleEndian>())),
        75 => OpCode::BinInt1(try!(rd.read_u8())),
        77 => OpCode::BinInt2(try!(rd.read_u16::<LittleEndian>())),
        76 => {
            let s = try!(read_until_newline(rd));

            let init = match s.split_last() {
                None => return Err(Error::InvalidString),
                Some((&76, init)) => init,
                Some(_) => return Err(Error::ExpectedTrailingL),
            };

            OpCode::Long(try!(try!(from_utf8(init)).parse()))
        },
        138 => {
            let length = try!(rd.read_u8());
            OpCode::Long1(try!(read_long(rd, length as usize)))

        },
        139 => {
            let length = try!(rd.read_i32::<LittleEndian>());
            OpCode::Long1(try!(read_long(rd, length as usize)))

        },
        c => return Err(Error::UnknownOpcode(c)),
    })
}

#[cfg(test)]
mod tests {
    use std::io::{Cursor};

    use num::{FromPrimitive};

    use super::{OpCode, Error, read_opcode};

    macro_rules! t {
        ($buffer: expr, $pat:pat, $result:expr) => ({
            match read_opcode(&mut Cursor::new(&$buffer[..])) {
                $pat => $result,
                other => {
                    println!("ERROR {:?}", other);
                    assert!(false)
                },
            }
        })
    }

    #[test]
    fn test_int() {
        t!(b"I", Err(Error::InvalidString), assert!(true));
        t!(b"I\n", Err(Error::InvalidInt), assert!(true));
        t!(b"Iabc\n", Err(Error::InvalidInt), assert!(true));
        t!(b"I123\n", Ok(OpCode::Int(n)), assert_eq!(n, FromPrimitive::from_usize(123).unwrap()));
    }

    #[test]
    fn test_bin_int() {
        t!(b"J\x0a", Err(Error::ReadError(_)), assert!(true));
        t!(b"J\x0a\x00\x00\x00", Ok(OpCode::BinInt(n)), assert_eq!(n, 10));
        t!(b"J\x0a\x00\x00\x01", Ok(OpCode::BinInt(n)), assert_eq!(n, 16777226));
    }

    #[test]
    fn test_bin_int1() {
        t!(b"K", Err(Error::ReadError(_)), assert!(true));
        t!(b"K\x0a", Ok(OpCode::BinInt1(n)), assert_eq!(n, 10));
    }

    #[test]
    fn test_bin_int2() {
        t!(b"M\x0a", Err(Error::ReadError(_)), assert!(true));
        t!(b"M\x0a\x00\x00\x00", Ok(OpCode::BinInt2(n)), assert_eq!(n, 10));
        t!(b"M\x0a\x01\x00\x00", Ok(OpCode::BinInt2(n)), assert_eq!(n, 266));
    }

    #[test]
    fn test_long() {
        t!(b"L", Err(Error::InvalidString), assert!(true));
        t!(b"L\n", Err(Error::InvalidString), assert!(true));
        t!(b"Labc\n", Err(Error::ExpectedTrailingL), assert!(true));
        t!(b"LabcL\n", Err(Error::InvalidInt), assert!(true));
        t!(b"L123L\n", Ok(OpCode::Long(n)), assert_eq!(n, FromPrimitive::from_usize(123).unwrap()));
    }

    #[test]
    fn test_long1() {
        t!(b"\x8a", Err(Error::ReadError(_)), assert!(true));
        t!(b"\x8a\x01\x0a", Ok(OpCode::Long1(n)), assert_eq!(n, FromPrimitive::from_usize(10).unwrap()));
        t!(b"\x8a\x01\xf6", Ok(OpCode::Long1(n)), assert_eq!(n, FromPrimitive::from_isize(-10).unwrap()));
        t!(b"\x8a\x02.\xfb", Ok(OpCode::Long1(n)), assert_eq!(n, FromPrimitive::from_isize(-1234).unwrap()));
    }

    #[test]
    fn test_long4() {
        t!(b"\x8b\x0a", Err(Error::ReadError(_)), assert!(true));
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
#![cfg(feature = "rand")]

extern crate num_bigint;
extern crate num_integer;
extern crate num_traits;
extern crate rand;
extern crate test;

use num_bigint::{BigInt, BigUint, RandBigInt};
use num_traits::{FromPrimitive, Num, One, Pow, Zero};
use rand::{SeedableRng, StdRng};
use std::mem::replace;
use test::Bencher;

fn get_rng() -> StdRng {
    let mut seed = [0; 32];
    for i in 1..32 {
        seed[usize::from(i)] = i;
    }
    SeedableRng::from_seed(seed)
}

fn multiply_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x * &y);
}

fn divide_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x / &y);
}

fn factorial(n: usize) -> BigUint {
    let mut f: BigUint = One::one();
    for i in 1..(n + 1) {
        let bu: BigUint = FromPrimitive::from_usize(i).unwrap();
        f = f * bu;
    }
    f
}

/// Compute Fibonacci numbers
fn fib(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        let f2 = f0 + &f1;
        f0 = replace(&mut f1, f2);
    }
    f0
}

/// Compute Fibonacci numbers with two ops per iteration
/// (add and subtract, like issue #200)
fn fib2(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        f1 = f1 + &f0;
        f0 = &f1 - f0;
    }
    f0
}

#[bench]
fn multiply_0(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 8);
}

#[bench]
fn multiply_1(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 16);
}

#[bench]
fn multiply_2(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 16);
}

#[bench]
fn multiply_3(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 17);
}

#[bench]
fn divide_0(b: &mut Bencher) {
    divide_bench(b, 1 << 8, 1 << 6);
}

#[bench]
fn divide_1(b: &mut Bencher) {
    divide_bench(b, 1 << 12, 1 << 8);
}

#[bench]
fn divide_2(b: &mut Bencher) {
    divide_bench(b, 1 << 16, 1 << 12);
}

#[bench]
fn factorial_100(b: &mut Bencher) {
    b.iter(|| factorial(100));
}

#[bench]
fn fib_100(b: &mut Bencher) {
    b.iter(|| fib(100));
}

#[bench]
fn fib_1000(b: &mut Bencher) {
    b.iter(|| fib(1000));
}

#[bench]
fn fib_10000(b: &mut Bencher) {
    b.iter(|| fib(10000));
}

#[bench]
fn fib2_100(b: &mut Bencher) {
    b.iter(|| fib2(100));
}

#[bench]
fn fib2_1000(b: &mut Bencher) {
    b.iter(|| fib2(1000));
}

#[bench]
fn fib2_10000(b: &mut Bencher) {
    b.iter(|| fib2(10000));
}

#[bench]
fn fac_to_string(b: &mut Bencher) {
    let fac = factorial(100);
    b.iter(|| fac.to_string());
}

#[bench]
fn fib_to_string(b: &mut Bencher) {
    let fib = fib(100);
    b.iter(|| fib.to_string());
}

fn to_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    b.iter(|| x.to_str_radix(radix));
}

#[bench]
fn to_str_radix_02(b: &mut Bencher) {
    to_str_radix_bench(b, 2);
}

#[bench]
fn to_str_radix_08(b: &mut Bencher) {
    to_str_radix_bench(b, 8);
}

#[bench]
fn to_str_radix_10(b: &mut Bencher) {
    to_str_radix_bench(b, 10);
}

#[bench]
fn to_str_radix_16(b: &mut Bencher) {
    to_str_radix_bench(b, 16);
}

#[bench]
fn to_str_radix_36(b: &mut Bencher) {
    to_str_radix_bench(b, 36);
}

fn from_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    let s = x.to_str_radix(radix);
    assert_eq!(x, BigInt::from_str_radix(&s, radix).unwrap());
    b.iter(|| BigInt::from_str_radix(&s, radix));
}

#[bench]
fn from_str_radix_02(b: &mut Bencher) {
    from_str_radix_bench(b, 2);
}

#[bench]
fn from_str_radix_08(b: &mut Bencher) {
    from_str_radix_bench(b, 8);
}

#[bench]
fn from_str_radix_10(b: &mut Bencher) {
    from_str_radix_bench(b, 10);
}

#[bench]
fn from_str_radix_16(b: &mut Bencher) {
    from_str_radix_bench(b, 16);
}

#[bench]
fn from_str_radix_36(b: &mut Bencher) {
    from_str_radix_bench(b, 36);
}

fn rand_bench(b: &mut Bencher, bits: usize) {
    let mut rng = get_rng();

    b.iter(|| rng.gen_bigint(bits));
}

#[bench]
fn rand_64(b: &mut Bencher) {
    rand_bench(b, 1 << 6);
}

#[bench]
fn rand_256(b: &mut Bencher) {
    rand_bench(b, 1 << 8);
}

#[bench]
fn rand_1009(b: &mut Bencher) {
    rand_bench(b, 1009);
}

#[bench]
fn rand_2048(b: &mut Bencher) {
    rand_bench(b, 1 << 11);
}

#[bench]
fn rand_4096(b: &mut Bencher) {
    rand_bench(b, 1 << 12);
}

#[bench]
fn rand_8192(b: &mut Bencher) {
    rand_bench(b, 1 << 13);
}

#[bench]
fn rand_65536(b: &mut Bencher) {
    rand_bench(b, 1 << 16);
}

#[bench]
fn rand_131072(b: &mut Bencher) {
    rand_bench(b, 1 << 17);
}

#[bench]
fn shl(b: &mut Bencher) {
    let n = BigUint::one() << 1000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m << i;
        }
    })
}

#[bench]
fn shr(b: &mut Bencher) {
    let n = BigUint::one() << 2000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m >> i;
        }
    })
}

#[bench]
fn hash(b: &mut Bencher) {
    use std::collections::HashSet;
    let mut rng = get_rng();
    let v: Vec<BigInt> = (1000..2000).map(|bits| rng.gen_bigint(bits)).collect();
    b.iter(|| {
        let h: HashSet<&BigInt> = v.iter().collect();
        assert_eq!(h.len(), v.len());
    });
}

#[bench]
fn pow_bench(b: &mut Bencher) {
    b.iter(|| {
        let upper = 100_usize;
        for i in 2..upper + 1 {
            for j in 2..upper + 1 {
                let i_big = BigUint::from_usize(i).unwrap();
                i_big.pow(j);
            }
        }
    });
}

/// This modulus is the prime from the 2048-bit MODP DH group:
/// https://tools.ietf.org/html/rfc3526#section-3
const RFC3526_2048BIT_MODP_GROUP: &'static str =
    "\
     FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
     29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
     EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
     E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
     EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
     C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
     83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
     670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
     E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
     DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
     15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF";

#[bench]
fn modpow(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap();

    b.iter(|| base.modpow(&e, &m));
}

#[bench]
fn modpow_even(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    // Make the modulus even, so monty (base-2^32) doesn't apply.
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap() - 1u32;

    b.iter(|| base.modpow(&e, &m));
}
<commit_msg>add rem benches<commit_after>#![feature(test)]
#![cfg(feature = "rand")]

extern crate num_bigint;
extern crate num_integer;
extern crate num_traits;
extern crate rand;
extern crate test;

use num_bigint::{BigInt, BigUint, RandBigInt};
use num_traits::{FromPrimitive, Num, One, Pow, Zero};
use rand::{SeedableRng, StdRng};
use std::mem::replace;
use test::Bencher;

fn get_rng() -> StdRng {
    let mut seed = [0; 32];
    for i in 1..32 {
        seed[usize::from(i)] = i;
    }
    SeedableRng::from_seed(seed)
}

fn multiply_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x * &y);
}

fn divide_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x / &y);
}

fn remainder_bench(b: &mut Bencher, xbits: usize, ybits: usize) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(xbits);
    let y = rng.gen_bigint(ybits);

    b.iter(|| &x % &y);
}

fn factorial(n: usize) -> BigUint {
    let mut f: BigUint = One::one();
    for i in 1..(n + 1) {
        let bu: BigUint = FromPrimitive::from_usize(i).unwrap();
        f = f * bu;
    }
    f
}

/// Compute Fibonacci numbers
fn fib(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        let f2 = f0 + &f1;
        f0 = replace(&mut f1, f2);
    }
    f0
}

/// Compute Fibonacci numbers with two ops per iteration
/// (add and subtract, like issue #200)
fn fib2(n: usize) -> BigUint {
    let mut f0: BigUint = Zero::zero();
    let mut f1: BigUint = One::one();
    for _ in 0..n {
        f1 = f1 + &f0;
        f0 = &f1 - f0;
    }
    f0
}

#[bench]
fn multiply_0(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 8);
}

#[bench]
fn multiply_1(b: &mut Bencher) {
    multiply_bench(b, 1 << 8, 1 << 16);
}

#[bench]
fn multiply_2(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 16);
}

#[bench]
fn multiply_3(b: &mut Bencher) {
    multiply_bench(b, 1 << 16, 1 << 17);
}

#[bench]
fn divide_0(b: &mut Bencher) {
    divide_bench(b, 1 << 8, 1 << 6);
}

#[bench]
fn divide_1(b: &mut Bencher) {
    divide_bench(b, 1 << 12, 1 << 8);
}

#[bench]
fn divide_2(b: &mut Bencher) {
    divide_bench(b, 1 << 16, 1 << 12);
}

#[bench]
fn remainder_0(b: &mut Bencher) {
    remainder_bench(b, 1 << 8, 1 << 6);
}

#[bench]
fn remainder_1(b: &mut Bencher) {
    remainder_bench(b, 1 << 12, 1 << 8);
}

#[bench]
fn remainder_2(b: &mut Bencher) {
    remainder_bench(b, 1 << 16, 1 << 12);
}

#[bench]
fn factorial_100(b: &mut Bencher) {
    b.iter(|| factorial(100));
}

#[bench]
fn fib_100(b: &mut Bencher) {
    b.iter(|| fib(100));
}

#[bench]
fn fib_1000(b: &mut Bencher) {
    b.iter(|| fib(1000));
}

#[bench]
fn fib_10000(b: &mut Bencher) {
    b.iter(|| fib(10000));
}

#[bench]
fn fib2_100(b: &mut Bencher) {
    b.iter(|| fib2(100));
}

#[bench]
fn fib2_1000(b: &mut Bencher) {
    b.iter(|| fib2(1000));
}

#[bench]
fn fib2_10000(b: &mut Bencher) {
    b.iter(|| fib2(10000));
}

#[bench]
fn fac_to_string(b: &mut Bencher) {
    let fac = factorial(100);
    b.iter(|| fac.to_string());
}

#[bench]
fn fib_to_string(b: &mut Bencher) {
    let fib = fib(100);
    b.iter(|| fib.to_string());
}

fn to_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    b.iter(|| x.to_str_radix(radix));
}

#[bench]
fn to_str_radix_02(b: &mut Bencher) {
    to_str_radix_bench(b, 2);
}

#[bench]
fn to_str_radix_08(b: &mut Bencher) {
    to_str_radix_bench(b, 8);
}

#[bench]
fn to_str_radix_10(b: &mut Bencher) {
    to_str_radix_bench(b, 10);
}

#[bench]
fn to_str_radix_16(b: &mut Bencher) {
    to_str_radix_bench(b, 16);
}

#[bench]
fn to_str_radix_36(b: &mut Bencher) {
    to_str_radix_bench(b, 36);
}

fn from_str_radix_bench(b: &mut Bencher, radix: u32) {
    let mut rng = get_rng();
    let x = rng.gen_bigint(1009);
    let s = x.to_str_radix(radix);
    assert_eq!(x, BigInt::from_str_radix(&s, radix).unwrap());
    b.iter(|| BigInt::from_str_radix(&s, radix));
}

#[bench]
fn from_str_radix_02(b: &mut Bencher) {
    from_str_radix_bench(b, 2);
}

#[bench]
fn from_str_radix_08(b: &mut Bencher) {
    from_str_radix_bench(b, 8);
}

#[bench]
fn from_str_radix_10(b: &mut Bencher) {
    from_str_radix_bench(b, 10);
}

#[bench]
fn from_str_radix_16(b: &mut Bencher) {
    from_str_radix_bench(b, 16);
}

#[bench]
fn from_str_radix_36(b: &mut Bencher) {
    from_str_radix_bench(b, 36);
}

fn rand_bench(b: &mut Bencher, bits: usize) {
    let mut rng = get_rng();

    b.iter(|| rng.gen_bigint(bits));
}

#[bench]
fn rand_64(b: &mut Bencher) {
    rand_bench(b, 1 << 6);
}

#[bench]
fn rand_256(b: &mut Bencher) {
    rand_bench(b, 1 << 8);
}

#[bench]
fn rand_1009(b: &mut Bencher) {
    rand_bench(b, 1009);
}

#[bench]
fn rand_2048(b: &mut Bencher) {
    rand_bench(b, 1 << 11);
}

#[bench]
fn rand_4096(b: &mut Bencher) {
    rand_bench(b, 1 << 12);
}

#[bench]
fn rand_8192(b: &mut Bencher) {
    rand_bench(b, 1 << 13);
}

#[bench]
fn rand_65536(b: &mut Bencher) {
    rand_bench(b, 1 << 16);
}

#[bench]
fn rand_131072(b: &mut Bencher) {
    rand_bench(b, 1 << 17);
}

#[bench]
fn shl(b: &mut Bencher) {
    let n = BigUint::one() << 1000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m << i;
        }
    })
}

#[bench]
fn shr(b: &mut Bencher) {
    let n = BigUint::one() << 2000;
    b.iter(|| {
        let mut m = n.clone();
        for i in 0..50 {
            m = m >> i;
        }
    })
}

#[bench]
fn hash(b: &mut Bencher) {
    use std::collections::HashSet;
    let mut rng = get_rng();
    let v: Vec<BigInt> = (1000..2000).map(|bits| rng.gen_bigint(bits)).collect();
    b.iter(|| {
        let h: HashSet<&BigInt> = v.iter().collect();
        assert_eq!(h.len(), v.len());
    });
}

#[bench]
fn pow_bench(b: &mut Bencher) {
    b.iter(|| {
        let upper = 100_usize;
        for i in 2..upper + 1 {
            for j in 2..upper + 1 {
                let i_big = BigUint::from_usize(i).unwrap();
                i_big.pow(j);
            }
        }
    });
}

/// This modulus is the prime from the 2048-bit MODP DH group:
/// https://tools.ietf.org/html/rfc3526#section-3
const RFC3526_2048BIT_MODP_GROUP: &'static str =
    "\
     FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
     29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
     EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
     E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
     EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
     C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
     83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
     670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
     E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
     DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
     15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF";

#[bench]
fn modpow(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap();

    b.iter(|| base.modpow(&e, &m));
}

#[bench]
fn modpow_even(b: &mut Bencher) {
    let mut rng = get_rng();
    let base = rng.gen_biguint(2048);
    let e = rng.gen_biguint(2048);
    // Make the modulus even, so monty (base-2^32) doesn't apply.
    let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap() - 1u32;

    b.iter(|| base.modpow(&e, &m));
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#![feature(test)]
extern crate parquet;
extern crate test;

use std::{collections::HashMap, fs::File, path::Path};

use parquet::{
  column::reader::{get_typed_column_reader, ColumnReader},
  data_type::*,
  file::reader::{FileReader, SerializedFileReader},
  schema::types::ColumnPath,
};

use test::Bencher;

#[bench]
fn record_reader_10k_collect(bench: &mut Bencher) {
  let path = Path::new("data/10k-v2.parquet");
  let file = File::open(&path).unwrap();
  let len = file.metadata().unwrap().len();
  let parquet_reader = SerializedFileReader::new(file).unwrap();

  bench.bytes = len;
  bench.iter(|| {
    let iter = parquet_reader.get_row_iter(None).unwrap();
    let _ = iter.collect::<Vec<_>>();
  })
}

#[bench]
fn record_reader_stock_simulated_collect(bench: &mut Bencher) {
  let path = Path::new("data/stock_simulated.parquet");
  let file = File::open(&path).unwrap();
  let len = file.metadata().unwrap().len();
  let parquet_reader = SerializedFileReader::new(file).unwrap();

  bench.bytes = len;
  bench.iter(|| {
    let iter = parquet_reader.get_row_iter(None).unwrap();
    let _ = iter.collect::<Vec<_>>();
  })
}

#[bench]
fn record_reader_stock_simulated_column(bench: &mut Bencher) {
  // WARNING THIS BENCH IS INTENDED FOR THIS DATA FILE ONLY
  // COPY OR CHANGE THE DATA FILE MAY NOT WORK AS YOU WISH
  let path = Path::new("data/stock_simulated.parquet");
  let file = File::open(&path).unwrap();
  let len = file.metadata().unwrap().len();
  let parquet_reader = SerializedFileReader::new(file).unwrap();

  let descr = parquet_reader.metadata().file_metadata().schema_descr_ptr();
  let num_row_groups = parquet_reader.num_row_groups();
  let batch_size = 256;

  bench.bytes = len;
  bench.iter(|| {
    let mut current_row_group = 0;

    while current_row_group < num_row_groups {
      let row_group_reader = parquet_reader.get_row_group(current_row_group).unwrap();
      let num_rows = row_group_reader.metadata().num_rows() as usize;

      let mut paths = HashMap::new();
      let row_group_metadata = row_group_reader.metadata();

      for col_index in 0..row_group_reader.num_columns() {
        let col_meta = row_group_metadata.column(col_index);
        let col_path = col_meta.column_path().clone();
        paths.insert(col_path, col_index);
      }

      let mut readers = Vec::new();
      for field in descr.root_schema().get_fields() {
        let col_path = ColumnPath::new(vec![field.name().to_owned()]);
        let orig_index = *paths.get(&col_path).unwrap();
        let col_reader = row_group_reader.get_column_reader(orig_index).unwrap();
        readers.push(col_reader);
      }

      let mut def_levels = Some(vec![0; batch_size]);
      let mut rep_levels = None::<Vec<i16>>;

      for col_reader in readers.into_iter() {
        match col_reader {
          r @ ColumnReader::Int64ColumnReader(..) => {
            let mut data_collected = Vec::with_capacity(num_rows);
            let mut val = vec![0; batch_size];
            let mut typed_reader = get_typed_column_reader::<Int64Type>(r);
            while let Ok((values_read, _levels_read)) = typed_reader.read_batch(
              batch_size,
              def_levels.as_mut().map(|x| &mut x[..]),
              rep_levels.as_mut().map(|x| &mut x[..]),
              &mut val,
            ) {
              data_collected.extend_from_slice(&val);
              if values_read < batch_size {
                break;
              }
            }
          },
          r @ ColumnReader::DoubleColumnReader(..) => {
            let mut data_collected = Vec::with_capacity(num_rows);
            let mut val = vec![0.0; batch_size];
            let mut typed_reader = get_typed_column_reader::<DoubleType>(r);
            while let Ok((values_read, _levels_read)) = typed_reader.read_batch(
              batch_size,
              def_levels.as_mut().map(|x| &mut x[..]),
              rep_levels.as_mut().map(|x| &mut x[..]),
              &mut val,
            ) {
              data_collected.extend_from_slice(&val);
              if values_read < batch_size {
                break;
              }
            }
          },
          _ => unimplemented!(),
        }
      }
      current_row_group += 1;
    }
  })
}
<commit_msg>Patch record_reader_10k_collect benchmark that fails with Int96 conversion (#202)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#![feature(test)]
extern crate parquet;
extern crate test;

use std::{collections::HashMap, fs::File, path::Path};

use parquet::{
  column::reader::{get_typed_column_reader, ColumnReader},
  data_type::*,
  file::reader::{FileReader, SerializedFileReader},
  schema::{parser::parse_message_type, types::ColumnPath},
};

use test::Bencher;

#[bench]
fn record_reader_10k_collect(bench: &mut Bencher) {
  let path = Path::new("data/10k-v2.parquet");
  let file = File::open(&path).unwrap();
  let len = file.metadata().unwrap().len();
  let parquet_reader = SerializedFileReader::new(file).unwrap();

  bench.bytes = len;
  bench.iter(|| {
    // TODO:
    // We pass projection that excludes Int96 field, otherwise we get the following error:
    //   Expected non-negative milliseconds when converting Int96, found -210866803200000
    // Field I removed:
    //   REQUIRED INT96 int96_field;
    // See issue #201 for more information and follow-up.
    let projection = parse_message_type(
      "
      message test {
        REQUIRED BYTE_ARRAY binary_field;
        REQUIRED INT32 int32_field;
        REQUIRED INT64 int64_field;
        REQUIRED BOOLEAN boolean_field;
        REQUIRED FLOAT float_field;
        REQUIRED DOUBLE double_field;
        REQUIRED FIXED_LEN_BYTE_ARRAY (1024) flba_field;
      }
    ",
    )
    .unwrap();
    let iter = parquet_reader.get_row_iter(Some(projection)).unwrap();
    let _ = iter.collect::<Vec<_>>();
  })
}

#[bench]
fn record_reader_stock_simulated_collect(bench: &mut Bencher) {
  let path = Path::new("data/stock_simulated.parquet");
  let file = File::open(&path).unwrap();
  let len = file.metadata().unwrap().len();
  let parquet_reader = SerializedFileReader::new(file).unwrap();

  bench.bytes = len;
  bench.iter(|| {
    let iter = parquet_reader.get_row_iter(None).unwrap();
    let _ = iter.collect::<Vec<_>>();
  })
}

#[bench]
fn record_reader_stock_simulated_column(bench: &mut Bencher) {
  // WARNING THIS BENCH IS INTENDED FOR THIS DATA FILE ONLY
  // COPY OR CHANGE THE DATA FILE MAY NOT WORK AS YOU WISH
  let path = Path::new("data/stock_simulated.parquet");
  let file = File::open(&path).unwrap();
  let len = file.metadata().unwrap().len();
  let parquet_reader = SerializedFileReader::new(file).unwrap();

  let descr = parquet_reader.metadata().file_metadata().schema_descr_ptr();
  let num_row_groups = parquet_reader.num_row_groups();
  let batch_size = 256;

  bench.bytes = len;
  bench.iter(|| {
    let mut current_row_group = 0;

    while current_row_group < num_row_groups {
      let row_group_reader = parquet_reader.get_row_group(current_row_group).unwrap();
      let num_rows = row_group_reader.metadata().num_rows() as usize;

      let mut paths = HashMap::new();
      let row_group_metadata = row_group_reader.metadata();

      for col_index in 0..row_group_reader.num_columns() {
        let col_meta = row_group_metadata.column(col_index);
        let col_path = col_meta.column_path().clone();
        paths.insert(col_path, col_index);
      }

      let mut readers = Vec::new();
      for field in descr.root_schema().get_fields() {
        let col_path = ColumnPath::new(vec![field.name().to_owned()]);
        let orig_index = *paths.get(&col_path).unwrap();
        let col_reader = row_group_reader.get_column_reader(orig_index).unwrap();
        readers.push(col_reader);
      }

      let mut def_levels = Some(vec![0; batch_size]);
      let mut rep_levels = None::<Vec<i16>>;

      for col_reader in readers.into_iter() {
        match col_reader {
          r @ ColumnReader::Int64ColumnReader(..) => {
            let mut data_collected = Vec::with_capacity(num_rows);
            let mut val = vec![0; batch_size];
            let mut typed_reader = get_typed_column_reader::<Int64Type>(r);
            while let Ok((values_read, _levels_read)) = typed_reader.read_batch(
              batch_size,
              def_levels.as_mut().map(|x| &mut x[..]),
              rep_levels.as_mut().map(|x| &mut x[..]),
              &mut val,
            ) {
              data_collected.extend_from_slice(&val);
              if values_read < batch_size {
                break;
              }
            }
          },
          r @ ColumnReader::DoubleColumnReader(..) => {
            let mut data_collected = Vec::with_capacity(num_rows);
            let mut val = vec![0.0; batch_size];
            let mut typed_reader = get_typed_column_reader::<DoubleType>(r);
            while let Ok((values_read, _levels_read)) = typed_reader.read_batch(
              batch_size,
              def_levels.as_mut().map(|x| &mut x[..]),
              rep_levels.as_mut().map(|x| &mut x[..]),
              &mut val,
            ) {
              data_collected.extend_from_slice(&val);
              if values_read < batch_size {
                break;
              }
            }
          },
          _ => unimplemented!(),
        }
      }
      current_row_group += 1;
    }
  })
}
<|endoftext|>