<commit_before>#![doc = r#"
# TODO

- improve `rsmt2` and make smt learner more efficient and less ad hoc
- remove all the useless error messages in lo-level rsmt2 writers
"#]

#![allow(non_upper_case_globals)]

#[macro_use]
extern crate lazy_static ;
#[macro_use]
extern crate mylib ;
#[macro_use]
extern crate error_chain ;
#[macro_use]
extern crate log ;
#[macro_use]
extern crate clap ;
extern crate ansi_term as ansi ;
extern crate regex ;
#[macro_use]
extern crate nom ;
extern crate hashconsing ;
#[macro_use]
extern crate rsmt2 ;
extern crate num ;
extern crate rayon ;

pub mod errors ;
#[macro_use]
pub mod common ;
pub mod instance ;
pub mod teacher ;
pub mod learning ;

pub mod check ;

use common::* ;

/// Parses command-line arguments and works.
pub fn work() -> Res<()> {

  // Creates smt log directory if needed.
  conf.init() ? ;

  let profiler = Profile::mk() ;

  if let Some(file_path) = conf.file.as_ref() {
    use std::fs::{ OpenOptions } ;
    use std::io::* ;

    // Are we in check mode?
    if let Some(output_file) = conf.check.as_ref() {
      return ::check::do_it(file_path, output_file)
    }

    profile!{ |profiler| tick "loading" }

    profile!{ |profiler| tick "loading", "reading file" }
    let mut txt = String::with_capacity(2000) ;
    let mut file = OpenOptions::new().read(true).open(file_path).chain_err(
      || format!("while opening input file `{}`", conf.emph(file_path))
    ) ? ;
    let _ = file.read_to_string(& mut txt).chain_err(
      || format!("while reading input file `{}`", conf.emph(file_path))
    ) ? ;
    profile!{ |profiler| mark "loading", "reading file" }

    let mut parser = ::instance::parse::Parser::mk(& txt) ;
    let mut instance = ::instance::Instance::mk(3_000, 42, 42) ;

    profile!{ |profiler| tick "loading", "parsing" }
    let mut parse_res = parser.parse( & mut instance ) ? ;
    profile!{ |profiler| mark "loading", "parsing" }

    log_info!{
      "instance:\n{}\n\n", instance.to_string_info(()) ?
    }

    profile!{ |profiler| tick "loading", "simplify" }
    instance.simplify_clauses() ? ;
    profile!{ |profiler| mark "loading", "simplify" }

    log_info!{
      "instance after simplification:\n{}\n\n",
      instance.to_string_info(()) ?
    }

    profile!{ |profiler| tick "loading", "reducing" }
    ::instance::reduction::work(& mut instance, & profiler) ? ;
    profile!{ |profiler| mark "loading", "reducing" }

    log_info!{
      "instance after reduction:\n{}\n\n", instance.to_string_info(()) ?
    }

    instance.finalize() ;

    profile!{ |profiler| mark "loading" }

    let mut model = None ;

    'parse_and_work: loop {

      use instance::parse::Parsed ;

      match parse_res {

        // Check-sat, start class.
        Parsed::CheckSat => {
          model = if let Some(model) = instance.is_trivial() ? {
            // Pre-processing already decided satisfiability.
            log_info!(
              "answering satisfiability query by pre-processing only"
            ) ;
            Some(model)
          } else {
            let arc_instance = Arc::new(instance) ;
            let partial_model = teacher::start_class(
              & arc_instance, & profiler
            ) ? ;
            { // Careful with this block, so that the unwrap does not fail
              while Arc::strong_count(& arc_instance) != 1 {}
              instance = if let Ok(
                instance
              ) = Arc::try_unwrap( arc_instance ) { instance } else {
                bail!("\
                  [bug] finalized teacher but there are still \
                  strong references to the instance\
                ")
              }
            }
            if let Some(partial_model) = partial_model {
              Some( instance.model_of(partial_model) ? )
            } else {
              None
            }
          } ;
          if model.is_some() {
            println!("sat")
          } else {
            println!("unsat")
          }

        },

        // Print model if available.
        Parsed::GetModel => if let Some(ref model) = model {
          let stdout = & mut ::std::io::stdout() ;
          println!("(model") ;
          for & (ref pred, ref tterms) in model {
            let pred_info = & instance[* pred] ;
            println!("  (define-fun {}", pred_info.name) ;
            print!(  "    (") ;
            for (var, typ) in pred_info.sig.index_iter() {
              print!(" ({} {})", instance.var(var), typ)
            }
            println!(" ) Bool") ;
            if tterms.len() > 1 {
              print!("    (and\n") ;
              for tterm in tterms {
                print!("      ") ;
                instance.print_tterm_as_model(stdout, tterm) ? ;
                println!("")
              }
              println!("    )")
            } else if tterms.len() == 1 {
              print!("    ") ;
              instance.print_tterm_as_model(stdout, & tterms[0]) ? ;
              println!("")
            } else {
              bail!(
                "model for predicate {} is empty",
                conf.sad( & pred_info.name )
              )
            }
            println!("  )")
          }
          println!(")")
        } else {
          bail!("no model available")
        },

        Parsed::None => break 'parse_and_work,
      }

      parse_res = parser.parse( & mut instance ) ? ;
    }

    print_stats(profiler) ;

    // let qualifiers = ::learning::ice::mining::Qualifiers::mk(& instance) ;

    // for pred in PrdRange::zero_to(
    //   instance.preds().len()
    // ) {
    //   println!("qualifiers for {}", conf.emph(& instance[pred].name) ) ;
    //   for qual in qualifiers.of(pred) {
    //     println!("  {}", qual)
    //   }
    //   println!("")
    // }

    Ok(())

  } else {
    log_info!{ "loading instance from `{}`...", conf.emph("stdin") }
    bail!("parsing from stdin is not implemented")
  }
}

/// Prints the stats if asked. Does nothing in bench mode.
#[cfg(feature = "bench")]
fn print_stats(_: Profile) {}
/// Prints the stats if asked. Does nothing in bench mode.
#[cfg( not(feature = "bench") )]
fn print_stats(profiler: Profile) {
  if conf.stats {
    let (tree, stats) = profiler.extract_tree() ;
    tree.print() ;
    if ! stats.is_empty() {
      println!("; stats:") ;
      stats.print()
    }
    println!("") ;
  }
}<commit_msg>Actually optional pre-processing<commit_after>#![doc = r#"
# TODO

- improve `rsmt2` and make smt learner more efficient and less ad hoc
- remove all the useless error messages in lo-level rsmt2 writers
"#]

#![allow(non_upper_case_globals)]

#[macro_use]
extern crate lazy_static ;
#[macro_use]
extern crate mylib ;
#[macro_use]
extern crate error_chain ;
#[macro_use]
extern crate log ;
#[macro_use]
extern crate clap ;
extern crate ansi_term as ansi ;
extern crate regex ;
#[macro_use]
extern crate nom ;
extern crate hashconsing ;
#[macro_use]
extern crate rsmt2 ;
extern crate num ;
extern crate rayon ;

pub mod errors ;
#[macro_use]
pub mod common ;
pub mod instance ;
pub mod teacher ;
pub mod learning ;

pub mod check ;

use common::* ;

/// Parses command-line arguments and works.
pub fn work() -> Res<()> {

  // Creates smt log directory if needed.
  conf.init() ? ;

  let profiler = Profile::mk() ;

  if let Some(file_path) = conf.file.as_ref() {
    use std::fs::{ OpenOptions } ;
    use std::io::* ;

    // Are we in check mode?
    if let Some(output_file) = conf.check.as_ref() {
      return ::check::do_it(file_path, output_file)
    }

    profile!{ |profiler| tick "loading" }

    profile!{ |profiler| tick "loading", "reading file" }
    let mut txt = String::with_capacity(2000) ;
    let mut file = OpenOptions::new().read(true).open(file_path).chain_err(
      || format!("while opening input file `{}`", conf.emph(file_path))
    ) ? ;
    let _ = file.read_to_string(& mut txt).chain_err(
      || format!("while reading input file `{}`", conf.emph(file_path))
    ) ? ;
    profile!{ |profiler| mark "loading", "reading file" }

    let mut parser = ::instance::parse::Parser::mk(& txt) ;
    let mut instance = ::instance::Instance::mk(3_000, 42, 42) ;

    profile!{ |profiler| tick "loading", "parsing" }
    let mut parse_res = parser.parse( & mut instance ) ? ;
    profile!{ |profiler| mark "loading", "parsing" }

    log_info!{
      "instance:\n{}\n\n", instance.to_string_info(()) ?
    }

    profile!{ |profiler| tick "loading", "simplify" }
    instance.simplify_clauses() ? ;
    profile!{ |profiler| mark "loading", "simplify" }

    log_info!{
      "instance after simplification:\n{}\n\n",
      instance.to_string_info(()) ?
    }

    if conf.pre_proc {
      profile!{ |profiler| tick "loading", "reducing" }
      ::instance::reduction::work(& mut instance, & profiler) ? ;
      profile!{ |profiler| mark "loading", "reducing" }
    }

    log_info!{
      "instance after reduction:\n{}\n\n", instance.to_string_info(()) ?
    }

    instance.finalize() ;

    profile!{ |profiler| mark "loading" }

    let mut model = None ;

    'parse_and_work: loop {

      use instance::parse::Parsed ;

      match parse_res {

        // Check-sat, start class.
        Parsed::CheckSat => {
          model = if let Some(model) = instance.is_trivial() ? {
            // Pre-processing already decided satisfiability.
            log_info!(
              "answering satisfiability query by pre-processing only"
            ) ;
            Some(model)
          } else {
            let arc_instance = Arc::new(instance) ;
            let partial_model = teacher::start_class(
              & arc_instance, & profiler
            ) ? ;
            { // Careful with this block, so that the unwrap does not fail
              while Arc::strong_count(& arc_instance) != 1 {}
              instance = if let Ok(
                instance
              ) = Arc::try_unwrap( arc_instance ) { instance } else {
                bail!("\
                  [bug] finalized teacher but there are still \
                  strong references to the instance\
                ")
              }
            }
            if let Some(partial_model) = partial_model {
              Some( instance.model_of(partial_model) ? )
            } else {
              None
            }
          } ;
          if model.is_some() {
            println!("sat")
          } else {
            println!("unsat")
          }

        },

        // Print model if available.
        Parsed::GetModel => if let Some(ref model) = model {
          let stdout = & mut ::std::io::stdout() ;
          println!("(model") ;
          for & (ref pred, ref tterms) in model {
            let pred_info = & instance[* pred] ;
            println!("  (define-fun {}", pred_info.name) ;
            print!(  "    (") ;
            for (var, typ) in pred_info.sig.index_iter() {
              print!(" ({} {})", instance.var(var), typ)
            }
            println!(" ) Bool") ;
            if tterms.len() > 1 {
              print!("    (and\n") ;
              for tterm in tterms {
                print!("      ") ;
                instance.print_tterm_as_model(stdout, tterm) ? ;
                println!("")
              }
              println!("    )")
            } else if tterms.len() == 1 {
              print!("    ") ;
              instance.print_tterm_as_model(stdout, & tterms[0]) ? ;
              println!("")
            } else {
              bail!(
                "model for predicate {} is empty",
                conf.sad( & pred_info.name )
              )
            }
            println!("  )")
          }
          println!(")")
        } else {
          bail!("no model available")
        },

        Parsed::None => break 'parse_and_work,
      }

      parse_res = parser.parse( & mut instance ) ? ;
    }

    print_stats(profiler) ;

    // let qualifiers = ::learning::ice::mining::Qualifiers::mk(& instance) ;

    // for pred in PrdRange::zero_to(
    //   instance.preds().len()
    // ) {
    //   println!("qualifiers for {}", conf.emph(& instance[pred].name) ) ;
    //   for qual in qualifiers.of(pred) {
    //     println!("  {}", qual)
    //   }
    //   println!("")
    // }

    Ok(())

  } else {
    log_info!{ "loading instance from `{}`...", conf.emph("stdin") }
    bail!("parsing from stdin is not implemented")
  }
}

/// Prints the stats if asked. Does nothing in bench mode.
#[cfg(feature = "bench")]
fn print_stats(_: Profile) {}
/// Prints the stats if asked. Does nothing in bench mode.
#[cfg( not(feature = "bench") )]
fn print_stats(profiler: Profile) {
  if conf.stats {
    let (tree, stats) = profiler.extract_tree() ;
    tree.print() ;
    if ! stats.is_empty() {
      println!("; stats:") ;
      stats.print()
    }
    println!("") ;
  }
}<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

<% data.new_style_struct("Text",
                         inherited=False,
                         gecko_name="TextReset",
                         additional_methods=[Method("has_underline", "bool"),
                                             Method("has_overline", "bool"),
                                             Method("has_line_through", "bool")]) %>

<%helpers:longhand name="text-overflow" animation_value_type="discrete" boxed="True"
                   flags="APPLIES_TO_PLACEHOLDER"
                   spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
    use std::fmt;
    use style_traits::ToCss;

    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
    pub enum Side {
        Clip,
        Ellipsis,
        String(Box<str>),
    }

    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
    pub struct SpecifiedValue {
        pub first: Side,
        pub second: Option<Side>
    }

    pub mod computed_value {
        pub use super::Side;

        #[derive(Clone, Debug, PartialEq)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T {
            // When the specified value only has one side, that's the "second"
            // side, and the sides are logical, so "second" means "end".  The
            // start side is Clip in that case.
            //
            // When the specified value has two sides, those are our "first"
            // and "second" sides, and they are physical sides ("left" and
            // "right").
            pub first: Side,
            pub second: Side,
            pub sides_are_logical: bool
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.sides_are_logical {
                assert!(self.first == Side::Clip);
                self.second.to_css(dest)?;
            } else {
                self.first.to_css(dest)?;
                dest.write_str(" ")?;
                self.second.to_css(dest)?;
            }
            Ok(())
        }
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
            if let Some(ref second) = self.second {
                Self::ComputedValue { first: self.first.clone(),
                                      second: second.clone(),
                                      sides_are_logical: false }
            } else {
                Self::ComputedValue { first: Side::Clip,
                                      second: self.first.clone(),
                                      sides_are_logical: true }
            }
        }

        #[inline]
        fn from_computed_value(computed: &Self::ComputedValue) -> Self {
            if computed.sides_are_logical {
                assert!(computed.first == Side::Clip);
                SpecifiedValue { first: computed.second.clone(),
                                 second: None }
            } else {
                SpecifiedValue { first: computed.first.clone(),
                                 second: Some(computed.second.clone()) }
            }
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            first: Side::Clip,
            second: Side::Clip,
            sides_are_logical: true,
        }
    }
    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let first = Side::parse(context, input)?;
        let second = input.try(|input| Side::parse(context, input)).ok();
        Ok(SpecifiedValue {
            first: first,
            second: second,
        })
    }
    impl Parse for Side {
        fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<Side, ParseError<'i>> {
            match *input.next()? {
                Token::Ident(ref ident) => {
                    try_match_ident_ignore_ascii_case! { ident,
                        "clip" => Ok(Side::Clip),
                        "ellipsis" => Ok(Side::Ellipsis),
                    }
                }
                Token::QuotedString(ref v) => {
                    Ok(Side::String(v.as_ref().to_owned().into_boxed_str()))
                }
                ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
            }
        }
    }
</%helpers:longhand>

${helpers.single_keyword("unicode-bidi",
                         "normal embed isolate bidi-override isolate-override plaintext",
                         animation_value_type="discrete",
                         need_clone="True",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi")}

<%helpers:longhand name="text-decoration-line"
                   custom_cascade="${product == 'servo'}"
                   need_clone=True
                   animation_value_type="discrete"
                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                   spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
    use std::fmt;
    use style_traits::ToCss;
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}

    bitflags! {
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub flags SpecifiedValue: u8 {
            const NONE = 0,
            const UNDERLINE = 0x01,
            const OVERLINE = 0x02,
            const LINE_THROUGH = 0x04,
            const BLINK = 0x08,
        % if product == "gecko":
            /// Only set by presentation attributes
            ///
            /// Setting this will mean that text-decorations use the color
            /// specified by `color` in quirks mode.
            ///
            /// For example, this gives text
            /// a red text decoration
            const COLOR_OVERRIDE = 0x10,
        % endif
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let mut has_any = false;

            macro_rules! write_value {
                ($line:ident => $css:expr) => {
                    if self.contains($line) {
                        if has_any {
                            dest.write_str(" ")?;
                        }
                        dest.write_str($css)?;
                        has_any = true;
                    }
                }
            }
            write_value!(UNDERLINE => "underline");
            write_value!(OVERLINE => "overline");
            write_value!(LINE_THROUGH => "line-through");
            write_value!(BLINK => "blink");
            if !has_any {
                dest.write_str("none")?;
            }

            Ok(())
        }
    }
    pub mod computed_value {
        pub type T = super::SpecifiedValue;
        #[allow(non_upper_case_globals)]
        pub const none: T = super::SpecifiedValue {
            bits: 0
        };
    }
    #[inline] pub fn get_initial_value() -> computed_value::T {
        computed_value::none
    }
    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        SpecifiedValue::empty()
    }
    /// none | [ underline || overline || line-through || blink ]
    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let mut result = SpecifiedValue::empty();
        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(result)
        }
        let mut empty = true;

        loop {
            let result: Result<_, ParseError> = input.try(|input| {
                match input.expect_ident() {
                    Ok(ident) => {
                        (match_ignore_ascii_case! { &ident,
                            "underline" => if result.contains(UNDERLINE) { Err(()) }
                                           else { empty = false; result.insert(UNDERLINE); Ok(()) },
                            "overline" => if result.contains(OVERLINE) { Err(()) }
                                          else { empty = false; result.insert(OVERLINE); Ok(()) },
                            "line-through" => if result.contains(LINE_THROUGH) { Err(()) }
                                              else { empty = false; result.insert(LINE_THROUGH); Ok(()) },
                            "blink" => if result.contains(BLINK) { Err(()) }
                                       else { empty = false; result.insert(BLINK); Ok(()) },
                            _ => Err(())
                        }).map_err(|()| SelectorParseError::UnexpectedIdent(ident.clone()).into())
                    }
                    Err(e) => return Err(e.into())
                }
            });
            if result.is_err() {
                break;
            }
        }

        if !empty { Ok(result) } else { Err(StyleParseError::UnspecifiedError.into()) }
    }

    % if product == "servo":
        fn cascade_property_custom(_declaration: &PropertyDeclaration,
                                   context: &mut computed::Context) {
            longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
        }
    % endif

    #[cfg(feature = "gecko")]
    impl_bitflags_conversions!(SpecifiedValue);
</%helpers:longhand>

${helpers.single_keyword("text-decoration-style",
                         "solid double dotted dashed wavy -moz-none",
                         products="gecko",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                         spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}

${helpers.predefined_type(
    "text-decoration-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color",
)}

${helpers.predefined_type(
    "initial-letter",
    "InitialLetter",
    "computed::InitialLetter::normal()",
    initial_specified_value="specified::InitialLetter::normal()",
    animation_value_type="discrete",
    products="gecko",
    flags="APPLIES_TO_FIRST_LETTER",
    spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials")}
<commit_msg>Do not use CVAS for the text-decoration-line property<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import Method %>

<% data.new_style_struct("Text",
                         inherited=False,
                         gecko_name="TextReset",
                         additional_methods=[Method("has_underline", "bool"),
                                             Method("has_overline", "bool"),
                                             Method("has_line_through", "bool")]) %>

<%helpers:longhand name="text-overflow" animation_value_type="discrete" boxed="True"
                   flags="APPLIES_TO_PLACEHOLDER"
                   spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow">
    use std::fmt;
    use style_traits::ToCss;

    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
    pub enum Side {
        Clip,
        Ellipsis,
        String(Box<str>),
    }

    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    #[derive(Clone, Debug, Eq, PartialEq, ToCss)]
    pub struct SpecifiedValue {
        pub first: Side,
        pub second: Option<Side>
    }

    pub mod computed_value {
        pub use super::Side;

        #[derive(Clone, Debug, PartialEq)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T {
            // When the specified value only has one side, that's the "second"
            // side, and the sides are logical, so "second" means "end".  The
            // start side is Clip in that case.
            //
            // When the specified value has two sides, those are our "first"
            // and "second" sides, and they are physical sides ("left" and
            // "right").
            pub first: Side,
            pub second: Side,
            pub sides_are_logical: bool
        }
    }

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.sides_are_logical {
                assert!(self.first == Side::Clip);
                self.second.to_css(dest)?;
            } else {
                self.first.to_css(dest)?;
                dest.write_str(" ")?;
                self.second.to_css(dest)?;
            }
            Ok(())
        }
    }

    impl ToComputedValue for SpecifiedValue {
        type ComputedValue = computed_value::T;

        #[inline]
        fn to_computed_value(&self, _context: &Context) -> Self::ComputedValue {
            if let Some(ref second) = self.second {
                Self::ComputedValue { first: self.first.clone(),
                                      second: second.clone(),
                                      sides_are_logical: false }
            } else {
                Self::ComputedValue { first: Side::Clip,
                                      second: self.first.clone(),
                                      sides_are_logical: true }
            }
        }

        #[inline]
        fn from_computed_value(computed: &Self::ComputedValue) -> Self {
            if computed.sides_are_logical {
                assert!(computed.first == Side::Clip);
                SpecifiedValue { first: computed.second.clone(),
                                 second: None }
            } else {
                SpecifiedValue { first: computed.first.clone(),
                                 second: Some(computed.second.clone()) }
            }
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            first: Side::Clip,
            second: Side::Clip,
            sides_are_logical: true,
        }
    }
    pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let first = Side::parse(context, input)?;
        let second = input.try(|input| Side::parse(context, input)).ok();
        Ok(SpecifiedValue {
            first: first,
            second: second,
        })
    }
    impl Parse for Side {
        fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<Side, ParseError<'i>> {
            match *input.next()? {
                Token::Ident(ref ident) => {
                    try_match_ident_ignore_ascii_case! { ident,
                        "clip" => Ok(Side::Clip),
                        "ellipsis" => Ok(Side::Ellipsis),
                    }
                }
                Token::QuotedString(ref v) => {
                    Ok(Side::String(v.as_ref().to_owned().into_boxed_str()))
                }
                ref t => Err(BasicParseError::UnexpectedToken(t.clone()).into()),
            }
        }
    }
</%helpers:longhand>

${helpers.single_keyword("unicode-bidi",
                         "normal embed isolate bidi-override isolate-override plaintext",
                         animation_value_type="discrete",
                         need_clone="True",
                         spec="https://drafts.csswg.org/css-writing-modes/#propdef-unicode-bidi")}

<%helpers:longhand name="text-decoration-line"
                   custom_cascade="${product == 'servo'}"
                   need_clone=True
                   animation_value_type="discrete"
                   flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                   spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-line">
    use std::fmt;
    use style_traits::ToCss;

    bitflags! {
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        #[derive(ToComputedValue)]
        pub flags SpecifiedValue: u8 {
            const NONE = 0,
            const UNDERLINE = 0x01,
            const OVERLINE = 0x02,
            const LINE_THROUGH = 0x04,
            const BLINK = 0x08,
        % if product == "gecko":
            /// Only set by presentation attributes
            ///
            /// Setting this will mean that text-decorations use the color
            /// specified by `color` in quirks mode.
            ///
            /// For example, this gives text
            /// a red text decoration
            const COLOR_OVERRIDE = 0x10,
        % endif
        }
    }

    impl ToCss for SpecifiedValue {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let mut has_any = false;

            macro_rules! write_value {
                ($line:ident => $css:expr) => {
                    if self.contains($line) {
                        if has_any {
                            dest.write_str(" ")?;
                        }
                        dest.write_str($css)?;
                        has_any = true;
                    }
                }
            }
            write_value!(UNDERLINE => "underline");
            write_value!(OVERLINE => "overline");
            write_value!(LINE_THROUGH => "line-through");
            write_value!(BLINK => "blink");
            if !has_any {
                dest.write_str("none")?;
            }

            Ok(())
        }
    }
    pub mod computed_value {
        pub type T = super::SpecifiedValue;
        #[allow(non_upper_case_globals)]
        pub const none: T = super::SpecifiedValue {
            bits: 0
        };
    }
    #[inline] pub fn get_initial_value() -> computed_value::T {
        computed_value::none
    }
    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        SpecifiedValue::empty()
    }
    /// none | [ underline || overline || line-through || blink ]
    pub fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>)
                         -> Result<SpecifiedValue, ParseError<'i>> {
        let mut result = SpecifiedValue::empty();
        if input.try(|input| input.expect_ident_matching("none")).is_ok() {
            return Ok(result)
        }
        let mut empty = true;

        loop {
            let result: Result<_, ParseError> = input.try(|input| {
                match input.expect_ident() {
                    Ok(ident) => {
                        (match_ignore_ascii_case! { &ident,
                            "underline" => if result.contains(UNDERLINE) { Err(()) }
                                           else { empty = false; result.insert(UNDERLINE); Ok(()) },
                            "overline" => if result.contains(OVERLINE) { Err(()) }
                                          else { empty = false; result.insert(OVERLINE); Ok(()) },
                            "line-through" => if result.contains(LINE_THROUGH) { Err(()) }
                                              else { empty = false; result.insert(LINE_THROUGH); Ok(()) },
                            "blink" => if result.contains(BLINK) { Err(()) }
                                       else { empty = false; result.insert(BLINK); Ok(()) },
                            _ => Err(())
                        }).map_err(|()| SelectorParseError::UnexpectedIdent(ident.clone()).into())
                    }
                    Err(e) => return Err(e.into())
                }
            });
            if result.is_err() {
                break;
            }
        }

        if !empty { Ok(result) } else { Err(StyleParseError::UnspecifiedError.into()) }
    }

    % if product == "servo":
        fn cascade_property_custom(_declaration: &PropertyDeclaration,
                                   context: &mut computed::Context) {
            longhands::_servo_text_decorations_in_effect::derive_from_text_decoration(context);
        }
    % endif

    #[cfg(feature = "gecko")]
    impl_bitflags_conversions!(SpecifiedValue);
</%helpers:longhand>

${helpers.single_keyword("text-decoration-style",
                         "solid double dotted dashed wavy -moz-none",
                         products="gecko",
                         animation_value_type="discrete",
                         flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                         spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-style")}

${helpers.predefined_type(
    "text-decoration-color",
    "Color",
    "computed_value::T::currentcolor()",
    initial_specified_value="specified::Color::currentcolor()",
    products="gecko",
    animation_value_type="AnimatedColor",
    ignored_when_colors_disabled=True,
    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration-color",
)}

${helpers.predefined_type(
    "initial-letter",
    "InitialLetter",
    "computed::InitialLetter::normal()",
    initial_specified_value="specified::InitialLetter::normal()",
    animation_value_type="discrete",
    products="gecko",
    flags="APPLIES_TO_FIRST_LETTER",
    spec="https://drafts.csswg.org/css-inline/#sizing-drop-initials")}
<|endoftext|>


<commit_before>use std::path::Path;

use tokio::sync::Mutex;

use crate::ipc_protocol::{
    ServerConnection,
    ServerResponse,
    ExportFormat,
};
use crate::renderer_client::ClientId;

use super::HandlerError;
use super::super::{
    access_control::{AccessControl, RequiredData, RequiredTurtles},
    renderer::{export, display_list::DisplayList},
};

pub(crate) async fn export_drawings(
    conn: &ServerConnection,
    client_id: ClientId,
    app_control: &AccessControl,
    display_list: &Mutex<DisplayList>,
    path: &Path,
    format: ExportFormat,
) -> Result<(), HandlerError> {
    // We need to lock everything to ensure that the export takes place in a sequentially
    // consistent way. We wouldn't want this to run while any lines are still being drawn.
    let mut data = app_control.get(RequiredData {
        drawing: true,
        turtles: Some(RequiredTurtles::All),
    }).await;

    // Wait to lock the display list until we actually have the data from the access controller
    let display_list = display_list.lock().await;

    use ExportFormat::*;
    let res = match format {
        Svg => export::save_svg(&display_list, data.drawing_mut(), path),
    };

    conn.send(client_id, ServerResponse::ExportComplete(res)).await?;

    Ok(())
}
<commit_msg>Locking turtles in export even though they aren't used (guarantees that mutation is impossible)<commit_after>use std::path::Path;

use tokio::sync::Mutex;

use crate::ipc_protocol::{
    ServerConnection,
    ServerResponse,
    ExportFormat,
};
use crate::renderer_client::ClientId;

use super::HandlerError;
use super::super::{
    access_control::{AccessControl, RequiredData, RequiredTurtles},
    renderer::{export, display_list::DisplayList},
};

pub(crate) async fn export_drawings(
    conn: &ServerConnection,
    client_id: ClientId,
    app_control: &AccessControl,
    display_list: &Mutex<DisplayList>,
    path: &Path,
    format: ExportFormat,
) -> Result<(), HandlerError> {
    // We need to lock everything to ensure that the export takes place in a sequentially
    // consistent way. We wouldn't want this to run while any lines are still being drawn.
    let mut data = app_control.get(RequiredData {
        drawing: true,
        turtles: Some(RequiredTurtles::All),
    }).await;
    // Lock the turtles even though they aren't used
    let mut _turtles = data.turtles_mut().await;

    // Wait to lock the display list until we actually have the data from the access controller
    let display_list = display_list.lock().await;

    use ExportFormat::*;
    let res = match format {
        Svg => export::save_svg(&display_list, data.drawing_mut(), path),
    };

    conn.send(client_id, ServerResponse::ExportComplete(res)).await?;

    Ok(())
}
<|endoftext|>


<commit_before>/*
* Huffman table generation:
* HuffTable::huff_decode()
* HuffTable::initialize()
* and used data structures are originally grabbed from the IJG software,
* and adapted by Hubert Figuiere.
*
* Copyright (C) 1991, 1992, Thomas G. Lane.
* Part of the Independent JPEG Group's software.
* See the file Copyright for more details.
*
* Copyright (c) 1993 Brian C. Smith, The Regents of the University
* of California
* All rights reserved.
*
* Copyright (c) 1994 Kongji Huang and Brian C. Smith.
* Cornell University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/

use std::fmt;
use decoders::basics::*;

const HUFF_BITMASK: [u32;32] = [0xffffffff, 0x7fffffff,
                                0x3fffffff, 0x1fffffff,
                                0x0fffffff, 0x07ffffff,
                                0x03ffffff, 0x01ffffff,
                                0x00ffffff, 0x007fffff,
                                0x003fffff, 0x001fffff,
                                0x000fffff, 0x0007ffff,
                                0x0003ffff, 0x0001ffff,
                                0x0000ffff, 0x00007fff,
                                0x00003fff, 0x00001fff,
                                0x00000fff, 0x000007ff,
                                0x000003ff, 0x000001ff,
                                0x000000ff, 0x0000007f,
                                0x0000003f, 0x0000001f,
                                0x0000000f, 0x00000007,
                                0x00000003, 0x00000001];

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private
  mincode: [u16;17],
  maxcode: [i32;18],
  valptr: [i16;17],
  numbits: [u32;256],
  bigtable: Vec<i32>,
  precision: usize,
  pub use_bigtable: bool,
  pub dng_compatible: bool,
  pub initialized: bool,
}

impl HuffTable {
  pub fn empty(precision: usize) -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      mincode: [0;17],
      maxcode: [0;18],
      valptr: [0;17],
      numbits: [0;256],
      bigtable: Vec::new(),
      precision: precision,
      use_bigtable: true,
      dng_compatible: true,
      initialized: false,
    }
  }

  pub fn initialize(&mut self, use_bigtable: bool) -> Result<(), String> {
    // Figure C.1: make table of Huffman code length for each symbol
    // Note that this is in code-length order.
    let mut p = 0;
    let mut huffsize: [u8;257] = [0;257];
    for l in 1..17 {
      for _ in 1..((self.bits[l] as usize)+1) {
        huffsize[p] = l as u8;
        p += 1;
        if p > 256 {
          return Err("ljpeg: Code length too long. Corrupt data.".to_string())
        }
      }
    }
    huffsize[p] = 0;
    let lastp = p;

    // Figure C.2: generate the codes themselves
    // Note that this is in code-length order.
    let mut code: u16 = 0;
    let mut si: u32 = huffsize[0] as u32;
    let mut huffcode: [u16;257] = [0;257];
    p = 0;
    while huffsize[p] > 0 {
      while (huffsize[p] as u32) == si {
        huffcode[p] = code;
        p += 1;
        code += 1;
      }
      code <<= 1;
      si += 1;
      if p > 256 {
        return Err("ljpeg: Code length too long. Corrupt data.".to_string())
      }
    }

    //Figure F.15: generate decoding tables
    self.mincode[0] = 0;
    self.maxcode[0] = 0;
    p = 0;
    for l in 1..17 {
      if self.bits[l] > 0 {
        self.valptr[l] = p as i16;
        self.mincode[l] = huffcode[p];
        p += self.bits[l] as usize;
        self.maxcode[l] = huffcode[p - 1] as i32;
      } else {
        self.valptr[l] = 0xff;   // This check must be present to avoid crash on junk
        self.maxcode[l] = -1;
      }
      if p > 256 {
        return Err("ljpeg: Code length too long. Corrupt data.".to_string())
      }
    }

    // We put in this value to ensure HuffDecode terminates
    self.maxcode[17] = 0xFFFFF;

    // Build the numbits, value lookup tables.
    // These table allow us to gather 8 bits from the bits stream,
    // and immediately lookup the size and value of the huffman codes.
    // If size is zero, it means that more than 8 bits are in the huffman
    // code (this happens about 3-4% of the time).
    for p in 0..lastp {
      let size = huffsize[p];
      if size <= 8 {
        let value: i32 = self.huffval[p] as i32;
        let code = huffcode[p];
        let ll: i32 = (code << (8 - size)) as i32;
        let ul: i32 = if size < 8 {
          ll | (HUFF_BITMASK[(24+size) as usize]) as i32
        } else {
          ll
        };
        if ul > 256 || ll > ul {
          return Err("ljpeg: Code length too long. Corrupt data.".to_string())
        }
        for i in ll..(ul+1) {
          self.numbits[i as usize] = (size as u32) | ((value as u32) << 4);
        }
      }
    }

    if use_bigtable {
      self.initialize_bigtable();
    }
    self.initialized = true;
    self.use_bigtable = use_bigtable;
    Ok(())
  }

  fn initialize_bigtable(&mut self) {
    let bits: usize = 14; // HuffDecode functions must be changed, if this is modified.
    let size: usize = 1 << bits;

    self.bigtable = vec![0 as i32;size];
    let mut rv: i32;
    for i in 0..size {
      let input = (i << 2) as u16;
      let mut code: i32 = (input >> 8) as i32;
      let val = self.numbits[code as usize];
      let mut l: u32 = val & 15;
      if l > 0 {
        rv = (val >> 4) as i32;
      } else {
        l = 8;
        while code > self.maxcode[l as usize] && l < 16 {
          let temp: i32 = (input >> (15 - l) & 1) as i32;
          code = (code << 1) | temp;
          l += 1;
        }

        //With garbage input we may reach the sentinel value l = 17.
        if l > self.precision as u32 || self.valptr[l as usize] == 0xff {
          self.bigtable[i] = 0xff;
          continue
        } else {
          rv = self.huffval[
            self.valptr[l as usize] as usize +
            (code - (self.mincode[l as usize] as i32)) as usize
          ] as i32;
        }
      }

      if rv == 16 {
        self.bigtable[i] = if self.dng_compatible {
          (-(32768 << 8)) | (16 + l as i32)
        } else {
          (-(32768 << 8)) | l as i32
        };
        continue
      }

      if rv + l as i32 > bits as i32 {
        self.bigtable[i] = 0xff;
        continue
      }

      if rv != 0 {
        let mut x = (input >> (16 - (l as i32) - rv) & ((1 << rv) - 1)) as i32;
        if (x & (1 << (rv - 1))) == 0 {
          x -= (1 << rv) - 1;
        }
        self.bigtable[i] = (x << 8) | ((l as i32) + rv);
      } else {
        self.bigtable[i] = l as i32;
      }
    }
    self.use_bigtable = true;
  }

  // Taken from Figure F.16: extract next coded symbol from input stream
  pub fn huff_decode(&self, pump: &mut BitPump) -> Result<i32,String> {
    //First attempt to do complete decode, by using the first 14 bits
    if self.use_bigtable {
      let code = pump.peek_bits(14) as usize;
      let val: i32 = self.bigtable[code];
      if val & 0xff != 0xff {
        pump.consume_bits((val & 0xff) as u32);
        return Ok(val >> 8)
      }
    }

    let len = try!(self.huff_len(pump));
    Ok(self.huff_diff(pump, len))
  }

  pub fn huff_len(&self, pump: &mut BitPump) -> Result<u32,String> {
    let mut code = pump.peek_bits(8) as usize;
    let val = self.numbits[code as usize] as u32;
    let len = val & 15;
    if len != 0 {
      pump.consume_bits(len);
      return Ok(val >> 4)
    }
    pump.consume_bits(8);
    let mut l: usize = 8;
    while code as i32 > self.maxcode[l] {
      let temp = pump.get_bits(1) as usize;
      code = (code << 1) | temp;
      l += 1;
    }

    // With garbage input we may reach the sentinel value l = 17.
    if l > self.precision || self.valptr[l] == 0xff {
      return Err(format!("ljpeg: bad Huffman code: {}", l).to_string())
    } else {
      return Ok(self.huffval[
        self.valptr[l] as usize +
        (code - (self.mincode[l] as usize)) as usize
      ]);
    }
  }

  pub fn huff_diff(&self, pump: &mut BitPump, len: u32) -> i32 {
    match len {
      0 => 0,
      16 => {
        if self.dng_compatible {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // Section F.2.2.1: decode the difference and
        // Figure F.12: extend sign bit
        let mut x: i32 = pump.get_bits(len as u32) as i32;
        if (x & (1 << (len - 1))) == 0 {
          x -= (1 << len) - 1;
        }
        x
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}

<commit_msg>ljpeg: make bigtable size configurable and smaller<commit_after>/*
* Huffman table generation:
* HuffTable::huff_decode()
* HuffTable::initialize()
* and used data structures are originally grabbed from the IJG software,
* and adapted by Hubert Figuiere.
*
* Copyright (C) 1991, 1992, Thomas G. Lane.
* Part of the Independent JPEG Group's software.
* See the file Copyright for more details.
*
* Copyright (c) 1993 Brian C. Smith, The Regents of the University
* of California
* All rights reserved.
*
* Copyright (c) 1994 Kongji Huang and Brian C. Smith.
* Cornell University
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose, without fee, and without written agreement is
* hereby granted, provided that the above copyright notice and the following
* two paragraphs appear in all copies of this software.
*
* IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
* OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL
* UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/

use std::fmt;
use decoders::basics::*;

const BIG_TABLE_BITS: u32 = 13;

const HUFF_BITMASK: [u32;32] = [0xffffffff, 0x7fffffff,
                                0x3fffffff, 0x1fffffff,
                                0x0fffffff, 0x07ffffff,
                                0x03ffffff, 0x01ffffff,
                                0x00ffffff, 0x007fffff,
                                0x003fffff, 0x001fffff,
                                0x000fffff, 0x0007ffff,
                                0x0003ffff, 0x0001ffff,
                                0x0000ffff, 0x00007fff,
                                0x00003fff, 0x00001fff,
                                0x00000fff, 0x000007ff,
                                0x000003ff, 0x000001ff,
                                0x000000ff, 0x0000007f,
                                0x0000003f, 0x0000001f,
                                0x0000000f, 0x00000007,
                                0x00000003, 0x00000001];

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private
  mincode: [u16;17],
  maxcode: [i32;18],
  valptr: [i16;17],
  numbits: [u32;256],
  bigtable: Vec<i32>,
  precision: usize,
  pub use_bigtable: bool,
  pub dng_compatible: bool,
  pub initialized: bool,
}

impl HuffTable {
  pub fn empty(precision: usize) -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      mincode: [0;17],
      maxcode: [0;18],
      valptr: [0;17],
      numbits: [0;256],
      bigtable: Vec::new(),
      precision: precision,
      use_bigtable: true,
      dng_compatible: true,
      initialized: false,
    }
  }

  pub fn initialize(&mut self, use_bigtable: bool) -> Result<(), String> {
    // Figure C.1: make table of Huffman code length for each symbol
    // Note that this is in code-length order.
    let mut p = 0;
    let mut huffsize: [u8;257] = [0;257];
    for l in 1..17 {
      for _ in 1..((self.bits[l] as usize)+1) {
        huffsize[p] = l as u8;
        p += 1;
        if p > 256 {
          return Err("ljpeg: Code length too long. Corrupt data.".to_string())
        }
      }
    }
    huffsize[p] = 0;
    let lastp = p;

    // Figure C.2: generate the codes themselves
    // Note that this is in code-length order.
    let mut code: u16 = 0;
    let mut si: u32 = huffsize[0] as u32;
    let mut huffcode: [u16;257] = [0;257];
    p = 0;
    while huffsize[p] > 0 {
      while (huffsize[p] as u32) == si {
        huffcode[p] = code;
        p += 1;
        code += 1;
      }
      code <<= 1;
      si += 1;
      if p > 256 {
        return Err("ljpeg: Code length too long. Corrupt data.".to_string())
      }
    }

    //Figure F.15: generate decoding tables
    self.mincode[0] = 0;
    self.maxcode[0] = 0;
    p = 0;
    for l in 1..17 {
      if self.bits[l] > 0 {
        self.valptr[l] = p as i16;
        self.mincode[l] = huffcode[p];
        p += self.bits[l] as usize;
        self.maxcode[l] = huffcode[p - 1] as i32;
      } else {
        self.valptr[l] = 0xff;   // This check must be present to avoid crash on junk
        self.maxcode[l] = -1;
      }
      if p > 256 {
        return Err("ljpeg: Code length too long. Corrupt data.".to_string())
      }
    }

    // We put in this value to ensure HuffDecode terminates
    self.maxcode[17] = 0xFFFFF;

    // Build the numbits, value lookup tables.
    // These table allow us to gather 8 bits from the bits stream,
    // and immediately lookup the size and value of the huffman codes.
    // If size is zero, it means that more than 8 bits are in the huffman
    // code (this happens about 3-4% of the time).
    for p in 0..lastp {
      let size = huffsize[p];
      if size <= 8 {
        let value: i32 = self.huffval[p] as i32;
        let code = huffcode[p];
        let ll: i32 = (code << (8 - size)) as i32;
        let ul: i32 = if size < 8 {
          ll | (HUFF_BITMASK[(24+size) as usize]) as i32
        } else {
          ll
        };
        if ul > 256 || ll > ul {
          return Err("ljpeg: Code length too long. Corrupt data.".to_string())
        }
        for i in ll..(ul+1) {
          self.numbits[i as usize] = (size as u32) | ((value as u32) << 4);
        }
      }
    }

    if use_bigtable {
      self.initialize_bigtable();
    }
    self.initialized = true;
    self.use_bigtable = use_bigtable;
    Ok(())
  }

  fn initialize_bigtable(&mut self) {
    let size: usize = 1 << BIG_TABLE_BITS;

    self.bigtable = vec![0 as i32;size];
    let mut rv: i32;
    for i in 0..size {
      let input = (i << (16-BIG_TABLE_BITS)) as u16;
      let mut code: i32 = (input >> 8) as i32;
      let val = self.numbits[code as usize];
      let mut l: u32 = val & 15;
      if l > 0 {
        rv = (val >> 4) as i32;
      } else {
        l = 8;
        while code > self.maxcode[l as usize] && l < 16 {
          let temp: i32 = (input >> (15 - l) & 1) as i32;
          code = (code << 1) | temp;
          l += 1;
        }

        //With garbage input we may reach the sentinel value l = 17.
        if l > self.precision as u32 || self.valptr[l as usize] == 0xff {
          self.bigtable[i] = 0xff;
          continue
        } else {
          rv = self.huffval[
            self.valptr[l as usize] as usize +
            (code - (self.mincode[l as usize] as i32)) as usize
          ] as i32;
        }
      }

      if rv == 16 {
        self.bigtable[i] = if self.dng_compatible {
          (-(32768 << 8)) | (16 + l as i32)
        } else {
          (-(32768 << 8)) | l as i32
        };
        continue
      }

      if rv + l as i32 > BIG_TABLE_BITS as i32 {
        self.bigtable[i] = 0xff;
        continue
      }

      if rv != 0 {
        let mut x = (input >> (16 - (l as i32) - rv) & ((1 << rv) - 1)) as i32;
        if (x & (1 << (rv - 1))) == 0 {
          x -= (1 << rv) - 1;
        }
        self.bigtable[i] = (x << 8) | ((l as i32) + rv);
      } else {
        self.bigtable[i] = l as i32;
      }
    }
    self.use_bigtable = true;
  }

  // Taken from Figure F.16: extract next coded symbol from input stream
  pub fn huff_decode(&self, pump: &mut BitPump) -> Result<i32,String> {
    //First attempt to do complete decode, by using the first 14 bits
    if self.use_bigtable {
      let code = pump.peek_bits(BIG_TABLE_BITS) as usize;
      let val: i32 = self.bigtable[code];
      if val & 0xff != 0xff {
        pump.consume_bits((val & 0xff) as u32);
        return Ok(val >> 8)
      }
    }

    let len = try!(self.huff_len(pump));
    Ok(self.huff_diff(pump, len))
  }

  pub fn huff_len(&self, pump: &mut BitPump) -> Result<u32,String> {
    let mut code = pump.peek_bits(8) as usize;
    let val = self.numbits[code as usize] as u32;
    let len = val & 15;
    if len != 0 {
      pump.consume_bits(len);
      return Ok(val >> 4)
    }
    pump.consume_bits(8);
    let mut l: usize = 8;
    while code as i32 > self.maxcode[l] {
      let temp = pump.get_bits(1) as usize;
      code = (code << 1) | temp;
      l += 1;
    }

    // With garbage input we may reach the sentinel value l = 17.
    if l > self.precision || self.valptr[l] == 0xff {
      return Err(format!("ljpeg: bad Huffman code: {}", l).to_string())
    } else {
      return Ok(self.huffval[
        self.valptr[l] as usize +
        (code - (self.mincode[l] as usize)) as usize
      ]);
    }
  }

  pub fn huff_diff(&self, pump: &mut BitPump, len: u32) -> i32 {
    match len {
      0 => 0,
      16 => {
        if self.dng_compatible {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // Section F.2.2.1: decode the difference and
        // Figure F.12: extend sign bit
        let mut x: i32 = pump.get_bits(len as u32) as i32;
        if (x & (1 << (len - 1))) == 0 {
          x -= (1 << len) - 1;
        }
        x
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}

<|endoftext|>


<commit_before>use std::fmt;
use std::cmp;
use crate::decoders::basics::*;

const DECODE_TABLE_BITS: u32 = 11;

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // Represent the weird shifts that are needed for some NEF files
  pub shiftval: [u32;256],

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private
  nbits: u32,
  hufftable: Vec<(u16,u16,u16)>,
  decodetable: [Option<(u16,i32)>; 1<< DECODE_TABLE_BITS],
  pub dng_bug: bool,
  pub initialized: bool,
}

struct MockPump {
  bits: u64,
  nbits: u32,
}

impl MockPump {
  pub fn empty() -> Self {
    MockPump {
      bits: 0,
      nbits: 0,
    }
  }

  pub fn set(&mut self, bits: u32, nbits: u32) {
    self.bits = (bits as u64) << 32;
    self.nbits = nbits + 32;
  }

  pub fn validbits(&self) -> i32 {
    self.nbits as i32 - 32
  }
}

impl BitPump for MockPump {
  fn peek_bits(&mut self, num: u32) -> u32 {
    (self.bits >> (self.nbits-num)) as u32
  }

  fn consume_bits(&mut self, num: u32) {
    self.nbits -= num;
    self.bits &= (1 << self.nbits) - 1;
  }
}

impl HuffTable {
  pub fn empty() -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      shiftval: [0;256],
      nbits: 0,
      hufftable: Vec::new(),
      decodetable: [None; 1 << DECODE_TABLE_BITS],
      dng_bug: false,
      initialized: false,
    }
  }

  pub fn new(bits: [u32;17], huffval: [u32;256], dng_bug: bool) -> Result<HuffTable,String> {
    let mut tbl = HuffTable {
      bits: bits,
      huffval: huffval,
      shiftval: [0;256],
      nbits: 0,
      hufftable: Vec::new(),
      decodetable: [None; 1 << DECODE_TABLE_BITS],
      dng_bug: dng_bug,
      initialized: false,
    };
    tbl.initialize()?;
    Ok(tbl)
  }

  pub fn initialize(&mut self) -> Result<(), String> {
    // Create the decoding table for the huffman lengths
    self.nbits = 16;
    for i in 0..16 {
      if self.bits[16-i] != 0 {
        break;
      }
      self.nbits -= 1;
    }

    let tblsize = 1 << self.nbits;
    self.hufftable = vec![(0,0,0); tblsize];

    let mut h = 0;
    let mut pos = 0;
    for len in 0..self.nbits {
      for _ in 0..self.bits[len as usize + 1] {
        for _ in 0..(1 << (self.nbits-len-1)) {
          self.hufftable[h] = (len as u16 + 1, self.huffval[pos] as u16, self.shiftval[pos] as u16);
          h += 1;
        }
        pos += 1;
      }
    }

    // Now bootstrap the full decode table
    let mut pump = MockPump::empty();
    let mut i = 0;
    loop {
      pump.set(i, DECODE_TABLE_BITS);
      let (lenbits, totalbits, decode) = self.huff_decode_slow(&mut pump);
      let validbits = pump.validbits();
      if validbits >= 0 {
        // We had a valid decode within the lookup bits, save that result to
        // every position where the decode applies.
        for _ in 0..(1 << validbits) {
          self.decodetable[i as usize] = Some((totalbits, decode));
          i += 1;
          if i >= 1 << DECODE_TABLE_BITS {
            break;
          }
        }
      } else {
        // We had an invalid decode, we can skip as many positions as the ones
        // that have the same bits for length
        i += 1 << cmp::max(0, DECODE_TABLE_BITS - (lenbits as u32));
      }
      if i >= 1 << DECODE_TABLE_BITS {
        break;
      }
    }

    self.initialized = true;
    Ok(())
  }

  pub fn huff_decode(&self, pump: &mut dyn BitPump) -> Result<i32,String> {
    let code = pump.peek_bits(DECODE_TABLE_BITS) as usize;
    if let Some((bits,decode)) = self.decodetable[code] {
      pump.consume_bits(bits as u32);
      Ok(decode)
    } else {
      let decode = self.huff_decode_slow(pump);
      Ok(decode.2)
    }
  }

  pub fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u16, u16,i32) {
    let len = self.huff_len(pump);
    (len.0, len.0+len.1, self.huff_diff(pump, len))
  }

  pub fn huff_len(&self, pump: &mut dyn BitPump) -> (u16,u16,u16) {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, shift) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    (bits, len, shift)
  }

  pub fn huff_diff(&self, pump: &mut dyn BitPump, input: (u16,u16,u16)) -> i32 {
    let (_, len, shift) = input;

    match len {
      0 => 0,
      16 => {
        if self.dng_bug {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // decode the difference and extend sign bit
        let fulllen: i32 = len as i32 + shift as i32;
        let shift: i32 = shift as i32;
        let bits = pump.get_bits(len as u32) as i32;
        let mut diff: i32 = ((bits << 1) + 1) << shift >> 1;
        if (diff & (1 << (fulllen - 1))) == 0 {
          diff -= (1 << fulllen) - ((shift == 0) as i32);
        }
        diff
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}
<commit_msg>Simplify huffman decode table generation<commit_after>use std::fmt;
use crate::decoders::basics::*;

const DECODE_TABLE_BITS: u32 = 11;

pub struct HuffTable {
  // These two fields directly represent the contents of a JPEG DHT marker
  pub bits: [u32;17],
  pub huffval: [u32;256],

  // Represent the weird shifts that are needed for some NEF files
  pub shiftval: [u32;256],

  // The remaining fields are computed from the above to allow more
  // efficient coding and decoding and thus private
  nbits: u32,
  hufftable: Vec<(u16,u16,u16)>,
  decodetable: [Option<(u16,i32)>; 1<< DECODE_TABLE_BITS],
  pub dng_bug: bool,
  pub initialized: bool,
}

struct MockPump {
  bits: u64,
  nbits: u32,
}

impl MockPump {
  pub fn empty() -> Self {
    MockPump {
      bits: 0,
      nbits: 0,
    }
  }

  pub fn set(&mut self, bits: u32, nbits: u32) {
    self.bits = (bits as u64) << 32;
    self.nbits = nbits + 32;
  }

  pub fn validbits(&self) -> i32 {
    self.nbits as i32 - 32
  }
}

impl BitPump for MockPump {
  fn peek_bits(&mut self, num: u32) -> u32 {
    (self.bits >> (self.nbits-num)) as u32
  }

  fn consume_bits(&mut self, num: u32) {
    self.nbits -= num;
    self.bits &= (1 << self.nbits) - 1;
  }
}

impl HuffTable {
  pub fn empty() -> HuffTable {
    HuffTable {
      bits: [0;17],
      huffval: [0;256],
      shiftval: [0;256],
      nbits: 0,
      hufftable: Vec::new(),
      decodetable: [None; 1 << DECODE_TABLE_BITS],
      dng_bug: false,
      initialized: false,
    }
  }

  pub fn new(bits: [u32;17], huffval: [u32;256], dng_bug: bool) -> Result<HuffTable,String> {
    let mut tbl = HuffTable {
      bits: bits,
      huffval: huffval,
      shiftval: [0;256],
      nbits: 0,
      hufftable: Vec::new(),
      decodetable: [None; 1 << DECODE_TABLE_BITS],
      dng_bug: dng_bug,
      initialized: false,
    };
    tbl.initialize()?;
    Ok(tbl)
  }

  pub fn initialize(&mut self) -> Result<(), String> {
    // Create the decoding table for the huffman lengths
    self.nbits = 16;
    for i in 0..16 {
      if self.bits[16-i] != 0 {
        break;
      }
      self.nbits -= 1;
    }

    let tblsize = 1 << self.nbits;
    self.hufftable = vec![(0,0,0); tblsize];

    let mut h = 0;
    let mut pos = 0;
    for len in 0..self.nbits {
      for _ in 0..self.bits[len as usize + 1] {
        for _ in 0..(1 << (self.nbits-len-1)) {
          self.hufftable[h] = (len as u16 + 1, self.huffval[pos] as u16, self.shiftval[pos] as u16);
          h += 1;
        }
        pos += 1;
      }
    }

    // Now bootstrap the full decode table
    let mut pump = MockPump::empty();
    let mut i = 0;
    loop {
      pump.set(i, DECODE_TABLE_BITS);
      let decode = self.huff_decode_slow(&mut pump);
      if pump.validbits() >= 0 {
        self.decodetable[i as usize] = Some(decode);
      }
      i += 1;
      if i >= 1 << DECODE_TABLE_BITS {
        break;
      }
    }

    self.initialized = true;
    Ok(())
  }

  pub fn huff_decode(&self, pump: &mut dyn BitPump) -> Result<i32,String> {
    let code = pump.peek_bits(DECODE_TABLE_BITS) as usize;
    if let Some((bits,decode)) = self.decodetable[code] {
      pump.consume_bits(bits as u32);
      Ok(decode)
    } else {
      let decode = self.huff_decode_slow(pump);
      Ok(decode.1)
    }
  }

  pub fn huff_decode_slow(&self, pump: &mut dyn BitPump) -> (u16,i32) {
    let len = self.huff_len(pump);
    (len.0+len.1, self.huff_diff(pump, len))
  }

  pub fn huff_len(&self, pump: &mut dyn BitPump) -> (u16,u16,u16) {
    let code = pump.peek_bits(self.nbits) as usize;
    let (bits, len, shift) = self.hufftable[code];
    pump.consume_bits(bits as u32);
    (bits, len, shift)
  }

  pub fn huff_diff(&self, pump: &mut dyn BitPump, input: (u16,u16,u16)) -> i32 {
    let (_, len, shift) = input;

    match len {
      0 => 0,
      16 => {
        if self.dng_bug {
          pump.get_bits(16); // consume can fail because we haven't peeked yet
        }
        -32768
      },
      len => {
        // decode the difference and extend sign bit
        let fulllen: i32 = len as i32 + shift as i32;
        let shift: i32 = shift as i32;
        let bits = pump.get_bits(len as u32) as i32;
        let mut diff: i32 = ((bits << 1) + 1) << shift >> 1;
        if (diff & (1 << (fulllen - 1))) == 0 {
          diff -= (1 << fulllen) - ((shift == 0) as i32);
        }
        diff
      },
    }
  }
}

impl fmt::Debug for HuffTable {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    if self.initialized {
      write!(f, "HuffTable {{ bits: {:?} huffval: {:?} }}", self.bits, &self.huffval[..])
    } else {
      write!(f, "HuffTable {{ uninitialized }}")
    }
  }
}
<|endoftext|>


<commit_before>//! An abstract input state object that gets fed user
//! events and updates itself based on a set of key
//! bindings.
//! The idea is threefold:
//!
//! * Have a layer of abstract key bindings rather than
//! looking at concrete event types
//! * Use this to be able to abstract away differences
//! between keyboards, joysticks and game controllers
//! (rather based on Unity3D),
//! * Do some tweening of input axes and stuff just for
//! fun maybe.
//!
//! Right now ggez doesn't handle joysticks or controllers
//! anyway, so.

use std::hash::Hash;
use std::collections::HashMap;
use ggez::event::*;

// Okay, but how does it actually work?
// Basically we have to bind input events to buttons and axes.
// Input events can be keys, mouse buttons/motion, or eventually
// joystick/controller inputs.  Mouse delta can be mapped to axes too.
//
// https://docs.unity3d.com/Manual/ConventionalGameInput.html has useful
// descriptions of the exact behavior of axes.
//
// So to think about this more clearly, here are the default bindings:
//
// W, ↑: +Y axis
// A, ←: -X axis
// S, ↓: -Y axis
// D, →: +X axis
// Enter, z, LMB: Button 1
// Shift, x, MMB: Button 2
// Ctrl,  c, RMB: Button 3
//
// Easy way?  Hash map of event -> axis/button bindings.

#[derive(Debug, Hash, Eq, PartialEq, Copy, Clone)]
enum InputEvent {
    KeyEvent(Keycode),
    MouseButtonEvent,
}

#[derive(Debug, Copy, Clone)]
enum InputEffect<Axes, Buttons>
    where Axes: Eq + Hash + Clone,
          Buttons: Eq + Hash + Clone
{
    Axis(Axes, bool),
    Button(Buttons),
}

#[derive(Debug)]
struct AxisStatus {
    // Where the axis currently is, in [-1, 1]
    position: f64,
    // Where the axis is moving towards.  Possible
    // values are -1, 0, +1
    // (or a continuous range for analog devices I guess)
    direction: f64,
    // Speed in units per second that the axis
    // moves towards the target value.
    acceleration: f64,
    // Speed in units per second that the axis will
    // fall back toward 0 if the input stops.
    gravity: f64,
}

impl Default for AxisStatus {
    fn default() -> Self {
        AxisStatus {
            position: 0.0,
            direction: 0.0,
            acceleration: 2.0,
            gravity: 2.0,
        }
    }
}

#[derive(Debug)]
pub struct InputManager<Axes, Buttons>
    where Axes: Hash + Eq + Clone,
          Buttons: Hash + Eq + Clone
{
    // Once EnumSet is stable it should be used for these
    // instead of BTreeMap. ♥?
    // Binding of keys to input values.
    bindings: HashMap<InputEvent, InputEffect<Axes, Buttons>>,
    // Input state for axes
    axes: HashMap<Axes, AxisStatus>,
    // Input states for buttons
    buttons: HashMap<Buttons, bool>,
}

impl<Axes, Buttons> InputManager<Axes, Buttons>
    where Axes: Eq + Hash + Clone,
          Buttons: Eq + Hash + Clone
{
    pub fn new() -> Self {
        InputManager {
            bindings: HashMap::new(),
            axes: HashMap::new(),
            buttons: HashMap::new(),
        }
    }

    /// Adds a key binding connecting the given keycode to the given
    /// logical axis.
    pub fn bind_key_to_axis(mut self, keycode: Keycode, axis: Axes, positive: bool) -> Self {

        self.bindings.insert(InputEvent::KeyEvent(keycode),
                             InputEffect::Axis(axis.clone(), positive));
        self.axes.insert(axis, AxisStatus::default());
        self
    }

    /// Adds a key binding connecting the given keycode to the given
    /// logical button.
    pub fn bind_key_to_button(mut self, keycode: Keycode, button: Buttons) -> Self {
        self.bindings.insert(InputEvent::KeyEvent(keycode),
                             InputEffect::Button(button.clone()));
        self.buttons.insert(button, false);
        self
    }

    /// Updates the logical input state based on the actual
    /// physical input state.  Should be called in your update()
    /// handler.
    /// So, it will do things like move the axes and so on.
    pub fn update(&mut self, dt: f64) {
        for (_axis, axis_status) in self.axes.iter_mut() {
            if axis_status.direction != 0.0 {
                // Accelerate the axis towards the
                // input'ed direction.
                let abs_dx = f64::min(axis_status.acceleration * dt,
                                      1.0 - f64::abs(axis_status.position));
                let dx = if axis_status.direction > 0.0 {
                    abs_dx
                } else {
                    -abs_dx
                };
                axis_status.position += dx;
            } else {
                // Gravitate back towards 0.
                let abs_dx = f64::min(axis_status.gravity * dt, f64::abs(axis_status.position));
                let dx = if axis_status.position > 0.0 {
                    -abs_dx
                } else {
                    abs_dx
                };
                axis_status.position += dx;
            }
        }
    }

    /// This method should get called by your key_down_event handler.
    pub fn update_keydown(&mut self, keycode: Option<Keycode>) {
        if let Some(keycode) = keycode {
            let effect = {
                if let Some(e) = self.bindings.get(&InputEvent::KeyEvent(keycode)) {
                    e.clone()
                } else {
                    return;
                }
            };
            self.update_effect(effect, true);
        }
    }

    /// This method should get called by your key_up_event handler.
    pub fn update_keyup(&mut self, keycode: Option<Keycode>) {
        if let Some(keycode) = keycode {
            let effect = {
                if let Some(e) = self.bindings.get(&InputEvent::KeyEvent(keycode)) {
                    e.clone()
                } else {
                    return;
                }
            };
            self.update_effect(effect, false);
        }
    }

    /// Takes an InputEffect and actually applies it.
    fn update_effect(&mut self, effect: InputEffect<Axes, Buttons>, started: bool) {
        match effect {
            InputEffect::Axis(axis, direction) => {
                let f = || AxisStatus::default();
                let axis_status = self.axes.entry(axis).or_insert_with(f);
                if started {
                    let direction_float = if direction {
                        1.0
                    } else {
                        -1.0
                    };
                    axis_status.direction = direction_float;
                } else {
                    axis_status.direction = 0.0;
                }
            }
            InputEffect::Button(button) => {
                let button_pressed = self.buttons.entry(button).or_insert(started);
                *button_pressed = started;

            }
        }
    }

    pub fn get_axis(&mut self, axis: Axes) -> f64 {
        let f = || AxisStatus::default();
        let axis_status = self.axes.entry(axis).or_insert_with(f);
        axis_status.position
    }

    pub fn get_axis_raw(&mut self, axis: Axes) -> f64 {
        let f = || AxisStatus::default();
        let axis_status = self.axes.entry(axis).or_insert_with(f);
        axis_status.direction
    }

    pub fn get_button(&self, axis: &Buttons) -> bool {
        if let Some(pressed) = self.buttons.get(axis) {
            *pressed
        } else {
            false
        }
    }

    pub fn get_button_down(&self, axis: &Buttons) -> bool {
        self.get_button(axis)
    }

    pub fn get_button_up(&self, axis: &Buttons) -> bool {
        !self.get_button(axis)
    }

    pub fn mouse_position() {}

    pub fn mouse_scroll_delta() {}

    pub fn get_mouse_button() {}

    pub fn get_mouse_button_down() {}

    pub fn get_mouse_button_up() {}

    pub fn reset_input_axes(&mut self) {
        for (_axis, axis_status) in self.axes.iter_mut() {
            axis_status.position = 0.0;
            axis_status.direction = 0.0;
        }
    }
}

mod tests {

    #[ignore(unused_imports)]
    use ggez::event::*;
    #[ignore(unused_imports)]
    use super::*;

    #[ignore(dead_code)]
    #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
    enum Buttons {
        A,
        B,
        Select,
        Start,
    }

    #[ignore(dead_code)]
    #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
    enum Axes {
        Horz,
        Vert,
    }
    #[test]
    fn test_input_events() {
        let mut im = InputManager::<Axes, Buttons>::new()
                         .bind_key_to_button(Keycode::Z, Buttons::A)
                         .bind_key_to_button(Keycode::X, Buttons::B)
                         .bind_key_to_button(Keycode::Return, Buttons::Start)
                         .bind_key_to_button(Keycode::RShift, Buttons::Select)
                         .bind_key_to_axis(Keycode::Up, Axes::Vert, true)
                         .bind_key_to_axis(Keycode::Down, Axes::Vert, false)
                         .bind_key_to_axis(Keycode::Left, Axes::Horz, false)
                         .bind_key_to_axis(Keycode::Right, Axes::Horz, true);

        im.update_keydown(Some(Keycode::Z));
        assert!(im.get_button(&Buttons::A));
        assert!(im.get_button_down(&Buttons::A));
        im.update_keyup(Some(Keycode::Z));
        assert!(!im.get_button(&Buttons::A));
        assert!(im.get_button_up(&Buttons::A));

        // Push the 'up' button, watch the axis
        // increase to 1.0 but not beyond
        im.update_keydown(Some(Keycode::Up));
        assert!(im.get_axis_raw(Axes::Vert) > 0.0);
        while im.get_axis(Axes::Vert) < 0.99 {
            im.update(0.16);
            assert!(im.get_axis(Axes::Vert) >= 0.0);
            assert!(im.get_axis(Axes::Vert) <= 1.0);
        }
        // Release it, watch it wind down
        im.update_keyup(Some(Keycode::Up));
        while im.get_axis(Axes::Vert) > 0.01 {
            im.update(0.16);
            assert!(im.get_axis(Axes::Vert) >= 0.0)
        }

        // Do the same with the 'down' button.
        im.update_keydown(Some(Keycode::Down));
        while im.get_axis(Axes::Vert) > -0.99 {
            im.update(0.16);
            assert!(im.get_axis(Axes::Vert) <= 0.0);
            assert!(im.get_axis(Axes::Vert) >= -1.0);
        }
    }
}
<commit_msg>Updated default input axis momentum values<commit_after>//! An abstract input state object that gets fed user
//! events and updates itself based on a set of key
//! bindings.
//! The idea is threefold:
//!
//! * Have a layer of abstract key bindings rather than
//! looking at concrete event types
//! * Use this to be able to abstract away differences
//! between keyboards, joysticks and game controllers
//! (rather based on Unity3D),
//! * Do some tweening of input axes and stuff just for
//! fun maybe.
//!
//! Right now ggez doesn't handle joysticks or controllers
//! anyway, so.

use std::hash::Hash;
use std::collections::HashMap;
use ggez::event::*;

// Okay, but how does it actually work?
// Basically we have to bind input events to buttons and axes.
// Input events can be keys, mouse buttons/motion, or eventually
// joystick/controller inputs.  Mouse delta can be mapped to axes too.
//
// https://docs.unity3d.com/Manual/ConventionalGameInput.html has useful
// descriptions of the exact behavior of axes.
//
// So to think about this more clearly, here are the default bindings:
//
// W, ↑: +Y axis
// A, ←: -X axis
// S, ↓: -Y axis
// D, →: +X axis
// Enter, z, LMB: Button 1
// Shift, x, MMB: Button 2
// Ctrl,  c, RMB: Button 3
//
// Easy way?  Hash map of event -> axis/button bindings.

#[derive(Debug, Hash, Eq, PartialEq, Copy, Clone)]
enum InputEvent {
    KeyEvent(Keycode),
    MouseButtonEvent,
}

#[derive(Debug, Copy, Clone)]
enum InputEffect<Axes, Buttons>
    where Axes: Eq + Hash + Clone,
          Buttons: Eq + Hash + Clone
{
    Axis(Axes, bool),
    Button(Buttons),
}

#[derive(Debug)]
struct AxisStatus {
    // Where the axis currently is, in [-1, 1]
    position: f64,
    // Where the axis is moving towards.  Possible
    // values are -1, 0, +1
    // (or a continuous range for analog devices I guess)
    direction: f64,
    // Speed in units per second that the axis
    // moves towards the target value.
    acceleration: f64,
    // Speed in units per second that the axis will
    // fall back toward 0 if the input stops.
    gravity: f64,
}

impl Default for AxisStatus {
    fn default() -> Self {
        AxisStatus {
            position: 0.0,
            direction: 0.0,
            acceleration: 4.0,
            gravity: 3.0,
        }
    }
}

#[derive(Debug)]
pub struct InputManager<Axes, Buttons>
    where Axes: Hash + Eq + Clone,
          Buttons: Hash + Eq + Clone
{
    // Once EnumSet is stable it should be used for these
    // instead of BTreeMap. ♥?
    // Binding of keys to input values.
    bindings: HashMap<InputEvent, InputEffect<Axes, Buttons>>,
    // Input state for axes
    axes: HashMap<Axes, AxisStatus>,
    // Input states for buttons
    buttons: HashMap<Buttons, bool>,
}

impl<Axes, Buttons> InputManager<Axes, Buttons>
    where Axes: Eq + Hash + Clone,
          Buttons: Eq + Hash + Clone
{
    pub fn new() -> Self {
        InputManager {
            bindings: HashMap::new(),
            axes: HashMap::new(),
            buttons: HashMap::new(),
        }
    }

    /// Adds a key binding connecting the given keycode to the given
    /// logical axis.
    pub fn bind_key_to_axis(mut self, keycode: Keycode, axis: Axes, positive: bool) -> Self {

        self.bindings.insert(InputEvent::KeyEvent(keycode),
                             InputEffect::Axis(axis.clone(), positive));
        self.axes.insert(axis, AxisStatus::default());
        self
    }

    /// Adds a key binding connecting the given keycode to the given
    /// logical button.
    pub fn bind_key_to_button(mut self, keycode: Keycode, button: Buttons) -> Self {
        self.bindings.insert(InputEvent::KeyEvent(keycode),
                             InputEffect::Button(button.clone()));
        self.buttons.insert(button, false);
        self
    }

    /// Updates the logical input state based on the actual
    /// physical input state.  Should be called in your update()
    /// handler.
    /// So, it will do things like move the axes and so on.
    pub fn update(&mut self, dt: f64) {
        for (_axis, axis_status) in self.axes.iter_mut() {
            if axis_status.direction != 0.0 {
                // Accelerate the axis towards the
                // input'ed direction.
                let abs_dx = f64::min(axis_status.acceleration * dt,
                                      1.0 - f64::abs(axis_status.position));
                let dx = if axis_status.direction > 0.0 {
                    abs_dx
                } else {
                    -abs_dx
                };
                axis_status.position += dx;
            } else {
                // Gravitate back towards 0.
                let abs_dx = f64::min(axis_status.gravity * dt, f64::abs(axis_status.position));
                let dx = if axis_status.position > 0.0 {
                    -abs_dx
                } else {
                    abs_dx
                };
                axis_status.position += dx;
            }
        }
    }

    /// This method should get called by your key_down_event handler.
    pub fn update_keydown(&mut self, keycode: Option<Keycode>) {
        if let Some(keycode) = keycode {
            let effect = {
                if let Some(e) = self.bindings.get(&InputEvent::KeyEvent(keycode)) {
                    e.clone()
                } else {
                    return;
                }
            };
            self.update_effect(effect, true);
        }
    }

    /// This method should get called by your key_up_event handler.
    pub fn update_keyup(&mut self, keycode: Option<Keycode>) {
        if let Some(keycode) = keycode {
            let effect = {
                if let Some(e) = self.bindings.get(&InputEvent::KeyEvent(keycode)) {
                    e.clone()
                } else {
                    return;
                }
            };
            self.update_effect(effect, false);
        }
    }

    /// Takes an InputEffect and actually applies it.
    fn update_effect(&mut self, effect: InputEffect<Axes, Buttons>, started: bool) {
        match effect {
            InputEffect::Axis(axis, direction) => {
                let f = || AxisStatus::default();
                let axis_status = self.axes.entry(axis).or_insert_with(f);
                if started {
                    let direction_float = if direction {
                        1.0
                    } else {
                        -1.0
                    };
                    axis_status.direction = direction_float;
                } else {
                    axis_status.direction = 0.0;
                }
            }
            InputEffect::Button(button) => {
                let button_pressed = self.buttons.entry(button).or_insert(started);
                *button_pressed = started;

            }
        }
    }

    pub fn get_axis(&mut self, axis: Axes) -> f64 {
        let f = || AxisStatus::default();
        let axis_status = self.axes.entry(axis).or_insert_with(f);
        axis_status.position
    }

    pub fn get_axis_raw(&mut self, axis: Axes) -> f64 {
        let f = || AxisStatus::default();
        let axis_status = self.axes.entry(axis).or_insert_with(f);
        axis_status.direction
    }

    pub fn get_button(&self, axis: &Buttons) -> bool {
        if let Some(pressed) = self.buttons.get(axis) {
            *pressed
        } else {
            false
        }
    }

    pub fn get_button_down(&self, axis: &Buttons) -> bool {
        self.get_button(axis)
    }

    pub fn get_button_up(&self, axis: &Buttons) -> bool {
        !self.get_button(axis)
    }

    pub fn mouse_position() {}

    pub fn mouse_scroll_delta() {}

    pub fn get_mouse_button() {}

    pub fn get_mouse_button_down() {}

    pub fn get_mouse_button_up() {}

    pub fn reset_input_axes(&mut self) {
        for (_axis, axis_status) in self.axes.iter_mut() {
            axis_status.position = 0.0;
            axis_status.direction = 0.0;
        }
    }
}

mod tests {

    #[ignore(unused_imports)]
    use ggez::event::*;
    #[ignore(unused_imports)]
    use super::*;

    #[ignore(dead_code)]
    #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
    enum Buttons {
        A,
        B,
        Select,
        Start,
    }

    #[ignore(dead_code)]
    #[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
    enum Axes {
        Horz,
        Vert,
    }
    #[test]
    fn test_input_events() {
        let mut im = InputManager::<Axes, Buttons>::new()
                         .bind_key_to_button(Keycode::Z, Buttons::A)
                         .bind_key_to_button(Keycode::X, Buttons::B)
                         .bind_key_to_button(Keycode::Return, Buttons::Start)
                         .bind_key_to_button(Keycode::RShift, Buttons::Select)
                         .bind_key_to_axis(Keycode::Up, Axes::Vert, true)
                         .bind_key_to_axis(Keycode::Down, Axes::Vert, false)
                         .bind_key_to_axis(Keycode::Left, Axes::Horz, false)
                         .bind_key_to_axis(Keycode::Right, Axes::Horz, true);

        im.update_keydown(Some(Keycode::Z));
        assert!(im.get_button(&Buttons::A));
        assert!(im.get_button_down(&Buttons::A));
        im.update_keyup(Some(Keycode::Z));
        assert!(!im.get_button(&Buttons::A));
        assert!(im.get_button_up(&Buttons::A));

        // Push the 'up' button, watch the axis
        // increase to 1.0 but not beyond
        im.update_keydown(Some(Keycode::Up));
        assert!(im.get_axis_raw(Axes::Vert) > 0.0);
        while im.get_axis(Axes::Vert) < 0.99 {
            im.update(0.16);
            assert!(im.get_axis(Axes::Vert) >= 0.0);
            assert!(im.get_axis(Axes::Vert) <= 1.0);
        }
        // Release it, watch it wind down
        im.update_keyup(Some(Keycode::Up));
        while im.get_axis(Axes::Vert) > 0.01 {
            im.update(0.16);
            assert!(im.get_axis(Axes::Vert) >= 0.0)
        }

        // Do the same with the 'down' button.
        im.update_keydown(Some(Keycode::Down));
        while im.get_axis(Axes::Vert) > -0.99 {
            im.update(0.16);
            assert!(im.get_axis(Axes::Vert) <= 0.0);
            assert!(im.get_axis(Axes::Vert) >= -1.0);
        }
    }
}
<|endoftext|>


<commit_before>#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Input {
    Character(char),
    Unknown(i32),
    KeyCodeYes,

    KeyBreak,
    KeyDown,
    KeyUp,
    KeyLeft,
    KeyRight,
    KeyHome,
    KeyBackspace,

    KeyF0,
    KeyF1,
    KeyF2,
    KeyF3,
    KeyF4,
    KeyF5,
    KeyF6,
    KeyF7,
    KeyF8,
    KeyF9,
    KeyF10,
    KeyF11,
    KeyF12,
    KeyF13,
    KeyF14,
    KeyF15,

    KeyDL,
    KeyIL,
    KeyDC,
    KeyIC,
    KeyEIC,
    KeyClear,
    KeyEOS,
    KeyEOL,
    KeySF,
    KeySR,
    KeyNPage,
    KeyPPage,
    KeySTab,
    KeyCTab,
    KeyCATab,
    KeyEnter,
    KeySReset,
    KeyReset,
    KeyPrint,
    KeyLL,
    KeyAbort,
    KeySHelp,
    KeyLHelp,
    KeyBTab,
    KeyBeg,
    KeyCancel,
    KeyClose,
    KeyCommand,
    KeyCopy,
    KeyCreate,
    KeyEnd,
    KeyExit,
    KeyFind,
    KeyHelp,
    KeyMark,
    KeyMessage,
    KeyMove,
    KeyNext,
    KeyOpen,
    KeyOptions,
    KeyPrevious,
    KeyRedo,
    KeyReference,
    KeyRefresh,
    KeyReplace,
    KeyRestart,
    KeyResume,
    KeySave,
    KeySBeg,
    KeySCancel,
    KeySCommand,
    KeySCopy,
    KeySCreate,
    KeySDC,
    KeySDL,
    KeySelect,
    KeySEnd,
    KeySEOL,
    KeySExit,
    KeySFind,
    KeySHome,
    KeySIC,

    KeySLeft,
    KeySMessage,
    KeySMove,
    KeySNext,
    KeySOptions,
    KeySPrevious,
    KeySPrint,
    KeySRedo,
    KeySReplace,
    KeySRight,
    KeySResume,
    KeySSave,
    KeySSuspend,
    KeySUndo,
    KeySuspend,
    KeyUndo,

    KeyResize,
    KeyEvent,
    KeyMouse,

    KeyA1,
    KeyA3,
    KeyB2,
    KeyC1,
    KeyC3,
}
<commit_msg>Add Eq and Hash traits to Input<commit_after>#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Input {
    Character(char),
    Unknown(i32),
    KeyCodeYes,

    KeyBreak,
    KeyDown,
    KeyUp,
    KeyLeft,
    KeyRight,
    KeyHome,
    KeyBackspace,

    KeyF0,
    KeyF1,
    KeyF2,
    KeyF3,
    KeyF4,
    KeyF5,
    KeyF6,
    KeyF7,
    KeyF8,
    KeyF9,
    KeyF10,
    KeyF11,
    KeyF12,
    KeyF13,
    KeyF14,
    KeyF15,

    KeyDL,
    KeyIL,
    KeyDC,
    KeyIC,
    KeyEIC,
    KeyClear,
    KeyEOS,
    KeyEOL,
    KeySF,
    KeySR,
    KeyNPage,
    KeyPPage,
    KeySTab,
    KeyCTab,
    KeyCATab,
    KeyEnter,
    KeySReset,
    KeyReset,
    KeyPrint,
    KeyLL,
    KeyAbort,
    KeySHelp,
    KeyLHelp,
    KeyBTab,
    KeyBeg,
    KeyCancel,
    KeyClose,
    KeyCommand,
    KeyCopy,
    KeyCreate,
    KeyEnd,
    KeyExit,
    KeyFind,
    KeyHelp,
    KeyMark,
    KeyMessage,
    KeyMove,
    KeyNext,
    KeyOpen,
    KeyOptions,
    KeyPrevious,
    KeyRedo,
    KeyReference,
    KeyRefresh,
    KeyReplace,
    KeyRestart,
    KeyResume,
    KeySave,
    KeySBeg,
    KeySCancel,
    KeySCommand,
    KeySCopy,
    KeySCreate,
    KeySDC,
    KeySDL,
    KeySelect,
    KeySEnd,
    KeySEOL,
    KeySExit,
    KeySFind,
    KeySHome,
    KeySIC,

    KeySLeft,
    KeySMessage,
    KeySMove,
    KeySNext,
    KeySOptions,
    KeySPrevious,
    KeySPrint,
    KeySRedo,
    KeySReplace,
    KeySRight,
    KeySResume,
    KeySSave,
    KeySSuspend,
    KeySUndo,
    KeySuspend,
    KeyUndo,

    KeyResize,
    KeyEvent,
    KeyMouse,

    KeyA1,
    KeyA3,
    KeyB2,
    KeyC1,
    KeyC3,
}
<|endoftext|>