<commit_before>
pub( crate ) mod internal
{
  use woptions::*;
  use std::collections::HashMap;

  ///
  /// Parsed request data.
  ///

  #[ allow( dead_code ) ]
  #[ derive( Debug, Default, PartialEq ) ]
  pub struct Request< 'a >
  {
    /// Original request string.
    pub original : &'a str,
    /// Delimeter for pairs `key:value`.
    pub key_val_delimeter : &'a str,
    /// Delimeter for commands.
    pub commands_delimeter : &'a str,
    /// Parsed subject of first command.
    pub subject : &'a str,
    /// All subjects of the commands in request.
    pub subjects : Vec<&'a str>,
    /// Options map of first command.
    pub map : HashMap<String, String>,
    /// All options maps of the commands in request.
    pub maps : Vec<HashMap<String, String>>,
  }

  ///
  /// Options for parser.
  ///

  #[ derive( Debug ) ]
  #[ derive( Former ) ]
  #[ perform( fn parse( mut self ) -> Request< 'a > ) ]
  pub struct ParseOptions< 'a >
  {
    #[ default( "" ) ]
    src : &'a str,
    #[ default( ":" ) ]
    key_val_delimeter : &'a str,
    #[ default( ";" ) ]
    commands_delimeter : &'a str,
    #[ default( true ) ]
    quoting : bool,
    #[ default( true ) ]
    unquoting : bool,
    #[ default( true ) ]
    parsing_arrays : bool,
    #[ default( false ) ]
    several_values : bool,
    #[ default( false ) ]
    subject_win_paths_maybe : bool,
  }

  ///
  /// Adapter for ParseOptions.
  ///

  pub trait ParseOptionsAdapter< 'a >
  {
    /// A string to parse.
    fn src( &self ) -> &'a str;
    /// A delimeter for pairs `key:value`.
    fn key_val_delimeter( &self ) -> &'a str;
    /// Delimeter for commands.
    fn commands_delimeter( &self ) -> &'a str;
    /// Quoting of strings.
    fn quoting( &self ) -> bool;
    /// Unquoting of string.
    fn unquoting( &self ) -> bool;
    /// Parse arrays of values.
    fn parsing_arrays( &self ) -> bool;
    /// Append to a vector a values.
    fn several_values( &self ) -> bool;
    /// Parse subject on Windows taking into account colon in path.
    fn subject_win_paths_maybe( &self ) -> bool;

    /// Do parsing.
    fn parse( self ) -> Request< 'a >
    where
      Self : Sized,
    {
      Request::default()
    }
  }

  impl< 'a > ParseOptionsAdapter< 'a > for ParseOptions< 'a >
  {
    fn src( &self ) -> &'a str
    {
      self.src
    }
    fn key_val_delimeter( &self ) -> &'a str
    {
      self.key_val_delimeter
    }
    fn commands_delimeter( &self ) -> &'a str
    {
      self.commands_delimeter
    }
    fn quoting( &self ) -> bool
    {
      self.quoting
    }
    fn unquoting( &self ) -> bool
    {
      self.unquoting
    }
    fn parsing_arrays( &self ) -> bool
    {
      self.parsing_arrays
    }
    fn several_values( &self ) -> bool
    {
      self.several_values
    }
    fn subject_win_paths_maybe( &self ) -> bool
    {
      self.subject_win_paths_maybe
    }

    fn parse( mut self ) -> Request< 'a >
    where
      Self : Sized,
    {
      let mut result = Request::default();

      result.original = self.src();
      result.key_val_delimeter = self.key_val_delimeter();
      result.commands_delimeter = self.commands_delimeter();

      println!("'{}' '{}'", self.src, self.src.trim());
      self.src = self.src.trim();

      if self.src.is_empty()
      {
        return result;
      }

      result
    }
  }

  ///
  /// Function to parse a string with command request.
  ///
  /// It produces former. To convert former into options and run algorithm of splitting call `perform()`.
  ///

  pub fn request_parse<'a>() -> ParseOptionsFormer<'a>
  {
    ParseOptions::former()
  }
}

/// Owned namespace of the module.
pub mod own
{
  use super::internal as i;

  pub use i::Request;
  pub use i::ParseOptions;
  pub use i::ParseOptionsAdapter;
  pub use i::request_parse;
}

pub use own::*;

/// Exposed namespace of the module.
pub mod exposed
{
  use super::internal as i;

  pub use i::ParseOptionsAdapter;
  pub use i::request_parse;
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  use super::internal as i;

  pub use i::ParseOptionsAdapter;
}
<commit_msg>Add module `parse` and implement routine `request_parse`<commit_after>
pub( crate ) mod internal
{
  use former::Former;
  use crate::string::split::*;
  use std::collections::HashMap;

  ///
  /// Parsed request data.
  ///

  #[ allow( dead_code ) ]
  #[ derive( Debug, Default, PartialEq ) ]
  pub struct Request< 'a >
  {
    /// Original request string.
    pub original : &'a str,
    /// Delimeter for pairs `key:value`.
    pub key_val_delimeter : &'a str,
    /// Delimeter for commands.
    pub commands_delimeter : &'a str,
    /// Parsed subject of first command.
    pub subject : String,
    /// All subjects of the commands in request.
    pub subjects : Vec<String>,
    /// Options map of first command.
    pub map : HashMap<String, String>,
    /// All options maps of the commands in request.
    pub maps : Vec<HashMap<String, String>>,
  }

  ///
  /// Options for parser.
  ///

  #[ derive( Debug ) ]
  #[ derive( Former ) ]
  #[ perform( fn parse( mut self ) -> Request< 'a > ) ]
  pub struct ParseOptions< 'a >
  {
    #[ default( "" ) ]
    src : &'a str,
    #[ default( ":" ) ]
    key_val_delimeter : &'a str,
    #[ default( ";" ) ]
    commands_delimeter : &'a str,
    #[ default( true ) ]
    quoting : bool,
    #[ default( true ) ]
    unquoting : bool,
    #[ default( true ) ]
    parsing_arrays : bool,
    #[ default( false ) ]
    several_values : bool,
    #[ default( false ) ]
    subject_win_paths_maybe : bool,
  }

  ///
  /// Adapter for ParseOptions.
  ///

  pub trait ParseOptionsAdapter< 'a >
  {
    /// A string to parse.
    fn src( &self ) -> &'a str;
    /// A delimeter for pairs `key:value`.
    fn key_val_delimeter( &self ) -> &'a str;
    /// Delimeter for commands.
    fn commands_delimeter( &self ) -> &'a str;
    /// Quoting of strings.
    fn quoting( &self ) -> bool;
    /// Unquoting of string.
    fn unquoting( &self ) -> bool;
    /// Parse arrays of values.
    fn parsing_arrays( &self ) -> bool;
    /// Append to a vector a values.
    fn several_values( &self ) -> bool;
    /// Parse subject on Windows taking into account colon in path.
    fn subject_win_paths_maybe( &self ) -> bool;

    /// Do parsing.
    fn parse( self ) -> Request< 'a >
    where
      Self : Sized,
    {
      Request::default()
    }
  }

  impl< 'a > ParseOptionsAdapter< 'a > for ParseOptions< 'a >
  {
    fn src( &self ) -> &'a str
    {
      self.src
    }
    fn key_val_delimeter( &self ) -> &'a str
    {
      self.key_val_delimeter
    }
    fn commands_delimeter( &self ) -> &'a str
    {
      self.commands_delimeter
    }
    fn quoting( &self ) -> bool
    {
      self.quoting
    }
    fn unquoting( &self ) -> bool
    {
      self.unquoting
    }
    fn parsing_arrays( &self ) -> bool
    {
      self.parsing_arrays
    }
    fn several_values( &self ) -> bool
    {
      self.several_values
    }
    fn subject_win_paths_maybe( &self ) -> bool
    {
      self.subject_win_paths_maybe
    }

    fn parse( mut self ) -> Request< 'a >
    where
      Self : Sized,
    {
      let mut result = Request::default();

      result.original = self.src();
      result.key_val_delimeter = self.key_val_delimeter();
      result.commands_delimeter = self.commands_delimeter();

      self.src = self.src.trim();

      if self.src.is_empty()
      {
        return result;
      }

      let commands;
      if self.commands_delimeter.trim().is_empty()
      {
        commands = vec![ self.src().to_string() ];
      }
      else
      {
        /* qqq : should use quoting */
        let iter = split()
        .src( self.src() )
        .delimeter( self.commands_delimeter() )
        // .quoting( self.quoting() )
        .stripping( true )
        .preserving_empty( false )
        .preserving_delimeters( false )
        .perform();
        commands = iter.map( | e | String::from( e ) ).collect::< Vec< _ > >();
      }

      for command in commands
      {
        let mut map_entries;
        if self.key_val_delimeter.trim().is_empty()
        {
          map_entries =  ( command.as_str(), None, "" );
        }
        else
        {
          map_entries = match command.split_once( self.key_val_delimeter )
          {
            Some( entries ) => ( entries.0, Some( self.key_val_delimeter ), entries.1 ),
            None => ( command.as_str(), None, "" ),
          };
        }

        let subject;
        let mut map : HashMap<String, String> = HashMap::new();

        if map_entries.1.is_some()
        {
          /* qqq : should be isolate_right* with option quoting */
          let subject_and_key = isolate_right_or_none( map_entries.0.trim(), " " );
          subject = subject_and_key.0;
          map_entries.0 = subject_and_key.2;

          let mut join = String::from( map_entries.0 );
          join.push_str( map_entries.1.unwrap() );
          join.push_str( map_entries.2 );

          /* qqq : implement preserving_quoting */
          let mut splits = split()
          .src( join.as_str() )
          .delimeter( self.key_val_delimeter )
          .stripping( false )
          // .quoting( self.quoting )
          .preserving_empty( true )
          .preserving_delimeters( true )
          // .preserving_quoting( true )
          .perform()
          .map( | e | String::from( e ) ).collect::< Vec< _ > >();

          let mut pairs = vec![];
          for a in ( 0..splits.len() - 2 ).step_by( 2 )
          {
            let mut right = splits[ a + 2 ].clone();

            while a < ( splits.len() - 3 )
            {
              let cuts = isolate_right_or_none( &right.trim(), " " );

              if cuts.1.is_none()
              {
                let mut joined = splits[ a + 2 ].clone();
                joined.push_str( splits[ a + 3 ].as_str() );
                joined.push_str( splits[ a + 4 ].as_str() );

                splits[ a + 2 ] = joined;
                right = splits[ a + 2 ].clone();
                splits.remove( a + 3 );
                splits.remove( a + 4 );
                continue;
              }

              splits[ a + 2 ] = cuts.2.to_string();
              right = cuts.0.to_string();
              break;
            }

            let left = splits[ a ].clone();
            let right = right.trim().to_string();
            if self.unquoting
            {
              if left.contains( "\"" ) || left.contains( "'" ) || right.contains( "\"" ) || right.contains( "'" )
              {
                unimplemented!( "not implemented" );
              }
              // left = str_unquote( left );
              // right = str_unquote( right );
            }

            pairs.push( left );
            pairs.push( right );
          }

          for a in ( 0..pairs.len() - 1 ).step_by( 2 )
          {
            let left = &pairs[ a ];
            let right = &pairs[ a + 1 ];
            // right = _.numberFromStrMaybe( right );
            // right = strToArrayMaybe( right );

            if self.several_values
            {
              unimplemented!( "not implemented" );
              // map[ left ] = _.scalarAppendOnce( map[ left ], right );
            }
            else
            {
              map.insert( left.to_string(), right.to_string() );
            }
          }
        }
        else
        {
          subject = map_entries.0;
        }

        if self.unquoting
        {
          if subject.contains( "\"" ) || subject.contains( "'" )
          {
            unimplemented!( "not implemented" );
          }
          // subject = _.strUnquote( subject );
        }

        if self.subject_win_paths_maybe
        {
          unimplemented!( "not implemented" );
          // subject = win_path_subject_check( subject, map );
        }

        result.subjects.push( subject.to_string() );
        result.maps.push( map );
      }

      if result.subjects.len() > 0
      {
        result.subject = result.subjects[ 0 ].clone();
      }
      if result.maps.len() > 0
      {
        result.map = result.maps[ 0 ].clone();
      }

      result
    }
  }

  fn isolate_right_or_none<'a>( src : &'a str, delimeter : &'a str ) -> ( &'a str, Option<&'a str>, &'a str )
  {
    let result = match src.trim().rsplit_once( delimeter )
    {
      Some( entries ) => ( entries.0, Some( delimeter ), entries.1 ),
      None => ( "", None, src ),
    };
    result
  }

  ///
  /// Function to parse a string with command request.
  ///
  /// It produces former. To convert former into options and run algorithm of splitting call `perform()`.
  ///

  pub fn request_parse<'a>() -> ParseOptionsFormer<'a>
  {
    ParseOptions::former()
  }
}

/// Owned namespace of the module.
pub mod own
{
  use super::internal as i;

  pub use i::Request;
  pub use i::ParseOptions;
  pub use i::ParseOptionsAdapter;
  pub use i::request_parse;
}

pub use own::*;

/// Exposed namespace of the module.
pub mod exposed
{
  use super::internal as i;

  pub use i::ParseOptionsAdapter;
  pub use i::request_parse;
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  use super::internal as i;

  pub use i::ParseOptionsAdapter;
}
<|endoftext|>


<commit_before>use cairo;
use glib::translate::*;
use glib_sys;
use libc;

use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{Parser, ParserInput, Token, ParseError as CssParseError};

use error::*;
use parsers::{Parse, ParseError, optional_comma};
use util::utf8_cstr;

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

// This parser is for the "transform" attribute in SVG.
// Its operataion and grammar are described here:
// https://www.w3.org/TR/SVG/coords.html#TransformAttribute

pub fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let matrix = parse_transform_list(s)?;

    matrix.try_invert ().map (|_| matrix)
        .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
}

fn parse_transform_list(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let mut matrix = cairo::Matrix::identity();

    loop {
        let m = parse_transform_command(&mut parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        if parser.is_exhausted() {
            break;
        }

        optional_comma(&mut parser);
    }

    Ok(matrix)
}

fn make_expected_function_error() -> AttributeError {
    AttributeError::from(ParseError::new("expected matrix|translate|scale|rotate|skewX|skewY"))
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match parser.next()?.clone() {
        Token::Function(ref name) => parse_transform_function(name, parser),

        Token::Ident(ref name) => {
            parser.expect_parenthesis_block()?;
            parse_transform_function(name, parser)
        },

        _ => Err(make_expected_function_error()),
    }
}

fn parse_transform_function(name: &str, parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match name {
        "matrix"    => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale"     => parse_scale_args(parser),
        "rotate"    => parse_rotate_args(parser),
        "skewX"     => parse_skewx_args(parser),
        "skewY"     => parse_skewy_args(parser),
        _           => Err(make_expected_function_error()),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let xx = f64::from(p.expect_number()?);
        optional_comma(p);

        let yx = f64::from(p.expect_number()?);
        optional_comma(p);

        let xy = f64::from(p.expect_number()?);
        optional_comma(p);

        let yy = f64::from(p.expect_number()?);
        optional_comma(p);

        let x0 = f64::from(p.expect_number()?);
        optional_comma(p);

        let y0 = f64::from(p.expect_number()?);

        Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let tx = f64::from(p.expect_number()?);

        let ty = f64::from(p.try(|p| -> Result<f32, CssParseError<()>> {
            optional_comma(p);
            Ok(p.expect_number()?)
        }).unwrap_or(0.0));

        Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let x = f64::from(p.expect_number()?);

        let y = p.try(|p| -> Result<f32, CssParseError<()>> {
            optional_comma(p);
            Ok(p.expect_number()?)
        }).map(f64::from).unwrap_or(x);

        Ok(cairo::Matrix::new(x, 0.0, 0.0, y, 0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let angle = f64::from(p.expect_number()?) * PI / 180.0;
        let (s, c) = angle.sin_cos();

        let (tx, ty) = p.try(|p| -> Result<_, CssParseError<()>> {
            optional_comma(p);
            let tx = f64::from(p.expect_number()?);

            optional_comma(p);
            let ty = f64::from(p.expect_number()?);

            Ok((tx, ty))
        }).unwrap_or((0.0, 0.0));

        let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

        m = cairo::Matrix::multiply (&cairo::Matrix::new (c, s, -s, c, 0.0, 0.0), &m);
        m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
        Ok(m)
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_skewx_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = f64::from(p.expect_number()?) * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,      0.0,
                               a.tan (), 1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

fn parse_skewy_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = f64::from(p.expect_number()?) * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,  a.tan (),
                               0.0,  1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(|e| AttributeError::from(e))
}

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { utf8_cstr(s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    fn assert_parse_error(s: &str) {
        match parse_transform(s) {
            Err(AttributeError::Parse(_)) => {},
            _ => { panic!(); }
        }
    }

    #[test]
    fn syntax_error_yields_parse_error () {
        assert_parse_error("foo");
        assert_parse_error("matrix (1 2 3 4 5)");
        assert_parse_error("translate(1 2 3 4 5)");
        assert_parse_error("translate (1,)");
        assert_parse_error("scale (1,)");
        assert_parse_error("skewX (1,2)");
        assert_parse_error("skewY ()");
        assert_parse_error("skewY");
    }

    #[test]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

#[cfg(test)]
mod parser_tests {
    use super::*;

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_transform ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix(1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    fn parses_translate () {
        assert_eq! (parse_transform ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1, -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    fn parses_scale() {
        assert_eq! (parse_transform ("scale (-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));
    }

    #[test]
    fn parses_rotate () {
        assert_eq! (parse_transform ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_transform ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_transform ("rotate(30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x () {
        assert_eq! (parse_transform ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    fn parses_skew_y () {
        assert_eq! (parse_transform ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_transform ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_transform ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<commit_msg>cargo-clippy: Remove redundant closures in transform.rs<commit_after>use cairo;
use glib::translate::*;
use glib_sys;
use libc;

use std::f64::consts::*;

use cairo::MatrixTrait;
use cssparser::{Parser, ParserInput, Token, ParseError as CssParseError};

use error::*;
use parsers::{Parse, ParseError, optional_comma};
use util::utf8_cstr;

impl Parse for cairo::Matrix {
    type Data = ();
    type Err = AttributeError;

    fn parse (s: &str, _: ()) -> Result<cairo::Matrix, AttributeError> {
        parse_transform (s)
    }
}

// This parser is for the "transform" attribute in SVG.
// Its operataion and grammar are described here:
// https://www.w3.org/TR/SVG/coords.html#TransformAttribute

pub fn parse_transform(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let matrix = parse_transform_list(s)?;

    matrix.try_invert ().map (|_| matrix)
        .map_err (|_| AttributeError::Value ("invalid transformation matrix".to_string ()))
}

fn parse_transform_list(s: &str) -> Result<cairo::Matrix, AttributeError> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let mut matrix = cairo::Matrix::identity();

    loop {
        let m = parse_transform_command(&mut parser)?;
        matrix = cairo::Matrix::multiply(&m, &matrix);

        if parser.is_exhausted() {
            break;
        }

        optional_comma(&mut parser);
    }

    Ok(matrix)
}

fn make_expected_function_error() -> AttributeError {
    AttributeError::from(ParseError::new("expected matrix|translate|scale|rotate|skewX|skewY"))
}

fn parse_transform_command(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match parser.next()?.clone() {
        Token::Function(ref name) => parse_transform_function(name, parser),

        Token::Ident(ref name) => {
            parser.expect_parenthesis_block()?;
            parse_transform_function(name, parser)
        },

        _ => Err(make_expected_function_error()),
    }
}

fn parse_transform_function(name: &str, parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    match name {
        "matrix"    => parse_matrix_args(parser),
        "translate" => parse_translate_args(parser),
        "scale"     => parse_scale_args(parser),
        "rotate"    => parse_rotate_args(parser),
        "skewX"     => parse_skewx_args(parser),
        "skewY"     => parse_skewy_args(parser),
        _           => Err(make_expected_function_error()),
    }
}

fn parse_matrix_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let xx = f64::from(p.expect_number()?);
        optional_comma(p);

        let yx = f64::from(p.expect_number()?);
        optional_comma(p);

        let xy = f64::from(p.expect_number()?);
        optional_comma(p);

        let yy = f64::from(p.expect_number()?);
        optional_comma(p);

        let x0 = f64::from(p.expect_number()?);
        optional_comma(p);

        let y0 = f64::from(p.expect_number()?);

        Ok(cairo::Matrix::new(xx, yx, xy, yy, x0, y0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_translate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let tx = f64::from(p.expect_number()?);

        let ty = f64::from(p.try(|p| -> Result<f32, CssParseError<()>> {
            optional_comma(p);
            Ok(p.expect_number()?)
        }).unwrap_or(0.0));

        Ok(cairo::Matrix::new(1.0, 0.0, 0.0, 1.0, tx, ty))
    }).map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_scale_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let x = f64::from(p.expect_number()?);

        let y = p.try(|p| -> Result<f32, CssParseError<()>> {
            optional_comma(p);
            Ok(p.expect_number()?)
        }).map(f64::from).unwrap_or(x);

        Ok(cairo::Matrix::new(x, 0.0, 0.0, y, 0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_rotate_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let angle = f64::from(p.expect_number()?) * PI / 180.0;
        let (s, c) = angle.sin_cos();

        let (tx, ty) = p.try(|p| -> Result<_, CssParseError<()>> {
            optional_comma(p);
            let tx = f64::from(p.expect_number()?);

            optional_comma(p);
            let ty = f64::from(p.expect_number()?);

            Ok((tx, ty))
        }).unwrap_or((0.0, 0.0));

        let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

        m = cairo::Matrix::multiply (&cairo::Matrix::new (c, s, -s, c, 0.0, 0.0), &m);
        m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
        Ok(m)
    }).map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_skewx_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = f64::from(p.expect_number()?) * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,      0.0,
                               a.tan (), 1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

fn parse_skewy_args(parser: &mut Parser) -> Result<cairo::Matrix, AttributeError> {
    parser.parse_nested_block(|p| {
        let a = f64::from(p.expect_number()?) * PI / 180.0;
        Ok(cairo::Matrix::new (1.0,  a.tan (),
                               0.0,  1.0,
                               0.0, 0.0))
    }).map_err(CssParseError::<()>::basic)
        .map_err(AttributeError::from)
}

#[cfg(test)]
fn make_rotation_matrix (angle_degrees: f64, tx: f64, ty: f64) -> cairo::Matrix {
    let angle = angle_degrees * PI / 180.0;

    let mut m = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, tx, ty);

    let mut r = cairo::Matrix::identity ();
    r.rotate (angle);
    m = cairo::Matrix::multiply (&r, &m);

    m = cairo::Matrix::multiply (&cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -tx, -ty), &m);
    m
}

#[no_mangle]
pub extern fn rsvg_parse_transform (out_matrix: *mut cairo::Matrix, s: *const libc::c_char) -> glib_sys::gboolean {
    assert! (!out_matrix.is_null ());
    assert! (!s.is_null ());

    let string = unsafe { utf8_cstr(s) };
    let matrix: &mut cairo::Matrix = unsafe { &mut *out_matrix };

    match parse_transform (string) {
        Ok (m) => {
            *matrix = m;
            true.to_glib ()
        },

        Err (_) => {
            *matrix = cairo::Matrix::identity ();
            false.to_glib ()
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn parses_valid_transform () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }

    fn assert_parse_error(s: &str) {
        match parse_transform(s) {
            Err(AttributeError::Parse(_)) => {},
            _ => { panic!(); }
        }
    }

    #[test]
    fn syntax_error_yields_parse_error () {
        assert_parse_error("foo");
        assert_parse_error("matrix (1 2 3 4 5)");
        assert_parse_error("translate(1 2 3 4 5)");
        assert_parse_error("translate (1,)");
        assert_parse_error("scale (1,)");
        assert_parse_error("skewX (1,2)");
        assert_parse_error("skewY ()");
        assert_parse_error("skewY");
    }

    #[test]
    fn invalid_transform_yields_value_error () {
        match parse_transform ("matrix (0 0 0 0 0 0)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), translate (10, 10)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }

        match parse_transform ("scale (0), skewX (90)") {
            Err (AttributeError::Value (_)) => {},
            _ => { panic! (); }
        }
    }
}

#[cfg(test)]
mod parser_tests {
    use super::*;

    #[test]
    fn parses_matrix () {
        assert_eq! (parse_transform ("matrix (1 2 3 4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix(1,2,3,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.0, 3.0, 4.0, 5.0, 6.0));

        assert_eq! (parse_transform ("matrix (1,2.25,-3.25e2,4 5 6)").unwrap (),
                    cairo::Matrix::new (1.0, 2.25, -325.0, 4.0, 5.0, 6.0));
    }

    #[test]
    fn parses_translate () {
        assert_eq! (parse_transform ("translate(-1 -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1, -2)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, -2.0));

        assert_eq! (parse_transform ("translate(-1)").unwrap (),
                    cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, -1.0, 0.0));
    }

    #[test]
    fn parses_scale() {
        assert_eq! (parse_transform ("scale (-1)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -1.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1 -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));

        assert_eq! (parse_transform ("scale(-1, -2)").unwrap (),
                    cairo::Matrix::new (-1.0, 0.0, 0.0, -2.0, 0.0, 0.0));
    }

    #[test]
    fn parses_rotate () {
        assert_eq! (parse_transform ("rotate (30)").unwrap (), make_rotation_matrix (30.0, 0.0, 0.0));
        assert_eq! (parse_transform ("rotate (30,-1,-2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
        assert_eq! (parse_transform ("rotate(30, -1, -2)").unwrap (), make_rotation_matrix (30.0, -1.0, -2.0));
    }

    fn make_skew_x_matrix (angle_degrees: f64) -> cairo::Matrix {
        let a = angle_degrees * PI / 180.0;
        cairo::Matrix::new (1.0,      0.0,
                            a.tan (), 1.0,
                            0.0, 0.0)
    }

    fn make_skew_y_matrix (angle_degrees: f64) -> cairo::Matrix {
        let mut m = make_skew_x_matrix (angle_degrees);
        m.yx = m.xy;
        m.xy = 0.0;
        m
    }

    #[test]
    fn parses_skew_x () {
        assert_eq! (parse_transform ("skewX (30)").unwrap (), make_skew_x_matrix (30.0));
    }

    #[test]
    fn parses_skew_y () {
        assert_eq! (parse_transform ("skewY (30)").unwrap (), make_skew_y_matrix (30.0));
    }

    #[test]
    fn parses_transform_list () {
        let t = cairo::Matrix::new (1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
        let s = cairo::Matrix::new (10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
        let r = make_rotation_matrix (30.0, 10.0, 10.0);

        assert_eq! (parse_transform ("scale(10)rotate(30, 10, 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &s));

        assert_eq! (parse_transform ("translate(20, 30), scale (10)").unwrap (),
                    cairo::Matrix::multiply (&s, &t));

        let a = cairo::Matrix::multiply (&s, &t);
        assert_eq! (parse_transform ("translate(20, 30), scale (10) rotate (30 10 10)").unwrap (),
                    cairo::Matrix::multiply (&r, &a));
    }
}
<|endoftext|>


<commit_before>extern crate timely;
extern crate differential_dataflow;
extern crate interactive;

extern crate serde;
#[macro_use]
extern crate serde_derive;

use timely::synchronization::Sequencer;
use interactive::{Manager, Command, Query, Rule, Plan};

#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Value {
    Bool(bool),
    Usize(usize),
    String(String),
    Address(Vec<usize>),
    Duration(::std::time::Duration),
}

use interactive::manager::AsVector;
use timely::logging::TimelyEvent;

impl AsVector<Value> for TimelyEvent {
    fn as_vector(self) -> Vec<Value> {
        match self {
            TimelyEvent::Operates(x) => {
                vec![Value::Usize(x.id), Value::Address(x.addr), Value::String(x.name)]
            },
            TimelyEvent::Channels(x) => {
                vec![Value::Usize(x.id), Value::Address(x.scope_addr), Value::Usize(x.source.0), Value::Usize(x.source.1), Value::Usize(x.target.0), Value::Usize(x.target.1)]
            },
            TimelyEvent::Schedule(x) => {
                vec![Value::Usize(x.id), Value::Bool(x.start_stop == ::timely::logging::StartStop::Start)]
            },
            TimelyEvent::Messages(x) => {
                vec![Value::Usize(x.channel), Value::Bool(x.is_send), Value::Usize(x.source), Value::Usize(x.target), Value::Usize(x.seq_no), Value::Usize(x.length)]
            },
            _ => { vec![] },
        }
    }
}

use differential_dataflow::logging::DifferentialEvent;

impl AsVector<Value> for DifferentialEvent {
    fn as_vector(self) -> Vec<Value> {
        match self {
            DifferentialEvent::Batch(x) => {
                vec![
                    Value::Usize(x.operator),
                    Value::Usize(x.length),
                ]
            },
            DifferentialEvent::Merge(x) => {
                vec![
                    Value::Usize(x.operator),
                    Value::Usize(x.scale),
                    Value::Usize(x.length1),
                    Value::Usize(x.length2),
                    Value::Usize(x.complete.unwrap_or(0)),
                    Value::Bool(x.complete.is_some()),
                ]
            },
            _ => { vec![] },
        }
    }
}

fn main() {

    let mut args = std::env::args();
    args.next();

    timely::execute_from_args(args, |worker| {

        let timer = ::std::time::Instant::now();
        let mut manager = Manager::new();

        use std::rc::Rc;
        use timely::dataflow::operators::capture::event::link::EventLink;
        use timely::logging::BatchLogger;

        // Capture timely logging events.
        let timely_events = Rc::new(EventLink::new());
        let mut timely_logger = BatchLogger::new(timely_events.clone());
        worker
            .log_register()
            .insert::<TimelyEvent,_>("timely", move |time, data| timely_logger.publish_batch(time, data));

        // Capture differential logging events.
        let differential_events = Rc::new(EventLink::new());
        let mut differential_logger = BatchLogger::new(differential_events.clone());
        worker
            .log_register()
            .insert::<DifferentialEvent,_>("differential/arrange", move |time, data| differential_logger.publish_batch(time, data));

        manager.publish_timely_logging(worker, Some(timely_events));
        manager.publish_differential_logging(worker, Some(differential_events));

        let mut sequencer = Some(Sequencer::new(worker, timer));

        if worker.index() == 0 {

            sequencer.as_mut().map(|x| x.push(Command::Query(
                Query {
                    rules: vec![
                        Rule {
                            name: "operates".to_string(),
                            plan: Plan::source("logs/timely/operates").inspect("operates:"),
                        },
                        Rule {
                            name: "channels".to_string(),
                            plan: Plan::source("logs/timely/channels").inspect("channels:"),
                        },
                        // Rule {
                        //     name: "schedule".to_string(),
                        //     plan: Plan::source("logs/timely/schedule").inspect("schedule:"),
                        // },
                        // Rule {
                        //     name: "messages".to_string(),
                        //     plan: Plan::source("logs/timely/messages").inspect("messages:"),
                        // },
                        // Rule {
                        //     name: "batch".to_string(),
                        //     plan: Plan::source("logs/differential/arrange/batch").inspect("batch:"),
                        // },
                        // Rule {
                        //     name: "merge".to_string(),
                        //     plan: Plan::source("logs/differential/arrange/merge").inspect("merge:"),
                        // },
                        Rule {
                            name: "active".to_string(),
                            plan: Plan::source("logs/timely/operates")
                                    .join(Plan::source("logs/differential/arrange/batch"), vec![(0,0)])
                                    .inspect("active"),
                        }
                    ]
                }
            )));

            // let edges = (0 .. 10).map(|x| vec![x, (x+1)%10]).collect::<Vec<_>>();

            // sequencer.as_mut().map(|x| x.push(Command::CreateInput("edges".to_string(), edges)));
            // sequencer.as_mut().map(|x| x.push(Command::AdvanceTime(timer.elapsed())));
            // sequencer.as_mut().map(|x| x.push(Command::Query(
            //     Query {
            //         rules: vec![Rule {
            //             name: "fof".to_string(),
            //             plan: Plan::source("edges").join(Plan::source("edges"), vec![(0,1)])
            //                                        .project(vec![1,2])
            //                                        .inspect("fof"),
            //         }]
            //     }
            // )));
            // sequencer.as_mut().map(|x| x.push(Command::AdvanceTime(timer.elapsed())));
            // sequencer.as_mut().map(|x| x.push(Command::CloseInput("edges".to_string())));
            // sequencer.as_mut().map(|x| x.push(Command::Query(
            //     Query {
            //         rules: vec![Rule {
            //             name: "fof2".to_string(),
            //             plan: Plan::source("fof").join(Plan::source("fof"), vec![(0,1)])
            //                                        .project(vec![1,2])
            //                                        .inspect("fof2"),
            //         }]
            //     }
            // )));

            // sequencer.as_mut().map(|x| x.push(Command::Shutdown));
        }

        while sequencer.is_some() {

            if let Some(command) = sequencer.as_mut().unwrap().next() {
                println!("{:?}\tExecuting {:?}", timer.elapsed(), command);
                if command == Command::Shutdown {

                    // Disable sequencer for shut down.
                    sequencer = None;

                    // Deregister the logger, so that the logging dataflow
                    // can shut down.
                    worker
                        .log_register()
                        .insert::<TimelyEvent,_>("timely", move |_time, _data| { });

                }
                command.execute(&mut manager, worker);
            }

            worker.step();
        }

    }).expect("Timely computation did not exit cleanly");
}
<commit_msg>server update<commit_after>extern crate timely;
extern crate differential_dataflow;
extern crate interactive;

extern crate serde;
#[macro_use]
extern crate serde_derive;

use timely::synchronization::Sequencer;
use interactive::{Manager, Command, Query, Rule, Plan};

#[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum Value {
    Bool(bool),
    Usize(usize),
    String(String),
    Address(Vec<usize>),
    Duration(::std::time::Duration),
}

use interactive::manager::AsVector;
use timely::logging::TimelyEvent;

impl AsVector<Value> for TimelyEvent {
    fn as_vector(self) -> Vec<Value> {
        match self {
            TimelyEvent::Operates(x) => {
                vec![Value::Usize(x.id), Value::Address(x.addr), Value::String(x.name)]
            },
            TimelyEvent::Channels(x) => {
                vec![Value::Usize(x.id), Value::Address(x.scope_addr), Value::Usize(x.source.0), Value::Usize(x.source.1), Value::Usize(x.target.0), Value::Usize(x.target.1)]
            },
            TimelyEvent::Schedule(x) => {
                vec![Value::Usize(x.id), Value::Bool(x.start_stop == ::timely::logging::StartStop::Start)]
            },
            TimelyEvent::Messages(x) => {
                vec![Value::Usize(x.channel), Value::Bool(x.is_send), Value::Usize(x.source), Value::Usize(x.target), Value::Usize(x.seq_no), Value::Usize(x.length)]
            },
            _ => { vec![] },
        }
    }
}

use differential_dataflow::logging::DifferentialEvent;

impl AsVector<Value> for DifferentialEvent {
    fn as_vector(self) -> Vec<Value> {
        match self {
            DifferentialEvent::Batch(x) => {
                vec![
                    Value::Usize(x.operator),
                    Value::Usize(x.length),
                ]
            },
            DifferentialEvent::Merge(x) => {
                vec![
                    Value::Usize(x.operator),
                    Value::Usize(x.scale),
                    Value::Usize(x.length1),
                    Value::Usize(x.length2),
                    Value::Usize(x.complete.unwrap_or(0)),
                    Value::Bool(x.complete.is_some()),
                ]
            },
            _ => { vec![] },
        }
    }
}

fn main() {

    let mut args = std::env::args();
    args.next();

    timely::execute_from_args(args, |worker| {

        let timer = ::std::time::Instant::now();
        let mut manager = Manager::<Value>::new();

        use std::rc::Rc;
        use timely::dataflow::operators::capture::event::link::EventLink;
        use timely::logging::BatchLogger;

        // Capture timely logging events.
        let timely_events = Rc::new(EventLink::new());
        // Capture differential logging events.
        let differential_events = Rc::new(EventLink::new());

        manager.publish_timely_logging(worker, Some(timely_events.clone()));
        manager.publish_differential_logging(worker, Some(differential_events.clone()));

        let mut timely_logger = BatchLogger::new(timely_events.clone());
        worker
            .log_register()
            .insert::<TimelyEvent,_>("timely", move |time, data| timely_logger.publish_batch(time, data));

        let mut differential_logger = BatchLogger::new(differential_events.clone());
        worker
            .log_register()
            .insert::<DifferentialEvent,_>("differential/arrange", move |time, data| differential_logger.publish_batch(time, data));

        let mut sequencer = Sequencer::new(worker, timer);

        if worker.index() == 0 {

            sequencer.push(Command::Query(
                Query {
                    rules: vec![
            //             Rule {
            //                 name: "operates".to_string(),
            //                 plan: Plan::source("logs/timely/operates").inspect("operates:"),
            //             },
            //             Rule {
            //                 name: "channels".to_string(),
            //                 plan: Plan::source("logs/timely/channels").inspect("channels:"),
            //             },
            //             Rule {
            //                 name: "schedule".to_string(),
            //                 plan: Plan::source("logs/timely/schedule").inspect("schedule:"),
            //             },
            //             Rule {
            //                 name: "messages".to_string(),
            //                 plan: Plan::source("logs/timely/messages").inspect("messages:"),
            //             },
            //             Rule {
            //                 name: "batch".to_string(),
            //                 plan: Plan::source("logs/differential/arrange/batch").inspect("batch:"),
            //             },
            //             Rule {
            //                 name: "merge".to_string(),
            //                 plan: Plan::source("logs/differential/arrange/merge").inspect("merge:"),
            //             },
            //             Rule {
            //                 name: "active".to_string(),
            //                 plan: Plan::source("logs/timely/operates")
            //                         .join(Plan::source("logs/differential/arrange/batch"), vec![(0,0)])
            //                         .inspect("active"),
            //             }
                    ]
                }
            ));

            sequencer.push(Command::Shutdown);
        }

        let mut shutdown = false;
        while !shutdown {

            if let Some(command) = sequencer.next() {
                println!("{:?}\tExecuting {:?}", timer.elapsed(), command);
                if command == Command::Shutdown {
                    shutdown = true;
                }
                command.execute(&mut manager, worker);
            }

            worker.step();
        }

        println!("Shutting down");

        // Disable sequencer for shut down.
        drop(sequencer);

        // Deregister loggers, so that the logging dataflows can shut down.
        worker
            .log_register()
            .insert::<TimelyEvent,_>("timely", move |_time, _data| { });

        worker
            .log_register()
            .insert::<DifferentialEvent,_>("differential/arrange", move |_time, _data| { });

    }).expect("Timely computation did not exit cleanly");
}
<|endoftext|>


<commit_before>use std::cmp;
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{Hash, Hasher};

/// A simple implementation of a [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog)
#[derive(Clone)]
pub struct HyperLogLog {
    registers: Vec<u8>,
    b: usize,
}

impl HyperLogLog {
    /// Creates a new, empty HyperLogLog.
    ///
    /// - `b` number of bits used for register selection, number of registers within the
    ///   HyperLogLog will be `2^b`. `b` must be in `[4, 16]`
    ///
    /// Panics when `b` is out of bounds.
    pub fn new(b: usize) -> HyperLogLog {
        assert!(b >= 4);
        assert!(b <= 16);

        let m = (1 as usize) << b;
        let registers = vec![0; m];
        HyperLogLog {
            registers: registers,
            b: b,
        }
    }

    /// Adds an element to the HyperLogLog.
    pub fn add<T>(&mut self, obj: &T)
    where
        T: Hash,
    {
        let mut hasher = DefaultHasher::new();
        obj.hash(&mut hasher);
        let h: u64 = hasher.finish();

        let w = h >> self.b;
        let j = h - (w << self.b);
        let p = w.leading_zeros() + 1 - (self.b as u32);

        let m_old = self.registers[j as usize];
        self.registers[j as usize] = cmp::max(m_old, p as u8);
    }

    /// Guess the number of unique elements seen by the HyperLogLog.
    pub fn count(&self) -> usize {
        let m = self.registers.len() as f64;

        let z = 1f64 /
            self.registers
                .iter()
                .map(|&x| 2f64.powi(-(x as i32)))
                .sum::<f64>();

        let am = if m >= 128. {
            0.7213 / (1. + 1.079 / m)
        } else if m >= 64. {
            0.709
        } else if m >= 32. {
            0.697
        } else {
            0.673
        };

        let e = am * m * m * z;

        let e_star = if e <= 5. / 2. * m {
            // small range correction
            let v = self.registers.iter().filter(|&&x| x == 0).count();
            if v != 0 {
                m * (m / (v as f64)).ln()
            } else {
                e
            }
        } else if e <= 1. / 30. * 2f64.powi(32) {
            // intermediate range => no correction
            e
        } else {
            // large range correction
            -2f64.powi(32) * (1. - e / 2f64.powi(32)).ln()
        };

        e_star as usize
    }

    /// Merge w/ another HyperLogLog.
    ///
    /// This HyperLogLog will then have the same state as if all elements seen by `other` where
    /// directly added to `self`.
    ///
    /// Panics when `b` parameter of `self` and `other` do not match.
    pub fn merge(&mut self, other: &HyperLogLog) {
        assert_eq!(self.b, other.b);

        self.registers = self.registers
            .iter()
            .zip(other.registers.iter())
            .map(|x| cmp::max(x.0, x.1))
            .cloned()
            .collect();
    }

    /// Empties the HyperLogLog.
    pub fn clear(&mut self) {
        self.registers = vec![0; self.registers.len()];
    }

    /// Checks whether the HyperLogLog has never seen an element.
    pub fn is_empty(&self) -> bool {
        self.registers.iter().all(|&x| x == 0)
    }
}

impl fmt::Debug for HyperLogLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HyperLogLog {{ b: {} }}", self.b)
    }
}

#[cfg(test)]
mod tests {
    use super::HyperLogLog;

    #[test]
    fn empty() {
        let hll = HyperLogLog::new(8);
        assert_eq!(hll.count(), 0);
        assert!(hll.is_empty());
    }

    #[test]
    fn add_b4_n1k() {
        let mut hll = HyperLogLog::new(4);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 571);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b8_n1k() {
        let mut hll = HyperLogLog::new(8);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 966);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b12_n1k() {
        let mut hll = HyperLogLog::new(12);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 984);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n1k() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 998);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b8_n10k() {
        let mut hll = HyperLogLog::new(8);
        for i in 0..10000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 10196);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b12_n10k() {
        let mut hll = HyperLogLog::new(12);
        for i in 0..10000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 10303);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n10k() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..10000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 10055);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n100k() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..100000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 100551);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n1m() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..1000000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 1000226);
        assert!(!hll.is_empty());
    }

    #[test]
    fn clear() {
        let mut hll = HyperLogLog::new(8);
        for i in 0..1000 {
            hll.add(&i);
        }
        hll.clear();
        assert_eq!(hll.count(), 0);
        assert!(hll.is_empty());
    }

    #[test]
    fn clone() {
        let mut hll1 = HyperLogLog::new(12);
        for i in 0..500 {
            hll1.add(&i);
        }
        let c1a = hll1.count();

        let hll2 = hll1.clone();
        assert_eq!(hll2.count(), c1a);

        for i in 501..1000 {
            hll1.add(&i);
        }
        let c1b = hll1.count();
        assert_ne!(c1b, c1a);
        assert_eq!(hll2.count(), c1a);
    }

    #[test]
    fn merge() {
        let mut hll1 = HyperLogLog::new(12);
        let mut hll2 = HyperLogLog::new(12);
        let mut hll = HyperLogLog::new(12);
        for i in 0..500 {
            hll.add(&i);
            hll1.add(&i);
        }
        for i in 501..1000 {
            hll.add(&i);
            hll2.add(&i);
        }
        assert_ne!(hll.count(), hll1.count());
        assert_ne!(hll.count(), hll2.count());

        hll1.merge(&hll2);
        assert_eq!(hll.count(), hll1.count());
    }

    #[test]
    fn debug() {
        let hll = HyperLogLog::new(12);
        assert_eq!(format!("{:?}", hll), "HyperLogLog { b: 12 }");
    }
}
<commit_msg>hyperloglog: impl getter<commit_after>use std::cmp;
use std::collections::hash_map::DefaultHasher;
use std::fmt;
use std::hash::{Hash, Hasher};

/// A simple implementation of a [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog)
#[derive(Clone)]
pub struct HyperLogLog {
    registers: Vec<u8>,
    b: usize,
}

impl HyperLogLog {
    /// Creates a new, empty HyperLogLog.
    ///
    /// - `b` number of bits used for register selection, number of registers within the
    ///   HyperLogLog will be `2^b`. `b` must be in `[4, 16]`
    ///
    /// Panics when `b` is out of bounds.
    pub fn new(b: usize) -> HyperLogLog {
        assert!(b >= 4);
        assert!(b <= 16);

        let m = (1 as usize) << b;
        let registers = vec![0; m];
        HyperLogLog {
            registers: registers,
            b: b,
        }
    }

    /// Get number of bits used for register selection.
    pub fn get_b(&self) -> usize {
        self.b
    }

    /// Get number of registers.
    pub fn get_m(&self) -> usize {
        self.registers.len()
    }

    /// Adds an element to the HyperLogLog.
    pub fn add<T>(&mut self, obj: &T)
    where
        T: Hash,
    {
        let mut hasher = DefaultHasher::new();
        obj.hash(&mut hasher);
        let h: u64 = hasher.finish();

        let w = h >> self.b;
        let j = h - (w << self.b);
        let p = w.leading_zeros() + 1 - (self.b as u32);

        let m_old = self.registers[j as usize];
        self.registers[j as usize] = cmp::max(m_old, p as u8);
    }

    /// Guess the number of unique elements seen by the HyperLogLog.
    pub fn count(&self) -> usize {
        let m = self.registers.len() as f64;

        let z = 1f64 /
            self.registers
                .iter()
                .map(|&x| 2f64.powi(-(x as i32)))
                .sum::<f64>();

        let am = if m >= 128. {
            0.7213 / (1. + 1.079 / m)
        } else if m >= 64. {
            0.709
        } else if m >= 32. {
            0.697
        } else {
            0.673
        };

        let e = am * m * m * z;

        let e_star = if e <= 5. / 2. * m {
            // small range correction
            let v = self.registers.iter().filter(|&&x| x == 0).count();
            if v != 0 {
                m * (m / (v as f64)).ln()
            } else {
                e
            }
        } else if e <= 1. / 30. * 2f64.powi(32) {
            // intermediate range => no correction
            e
        } else {
            // large range correction
            -2f64.powi(32) * (1. - e / 2f64.powi(32)).ln()
        };

        e_star as usize
    }

    /// Merge w/ another HyperLogLog.
    ///
    /// This HyperLogLog will then have the same state as if all elements seen by `other` where
    /// directly added to `self`.
    ///
    /// Panics when `b` parameter of `self` and `other` do not match.
    pub fn merge(&mut self, other: &HyperLogLog) {
        assert_eq!(self.b, other.b);

        self.registers = self.registers
            .iter()
            .zip(other.registers.iter())
            .map(|x| cmp::max(x.0, x.1))
            .cloned()
            .collect();
    }

    /// Empties the HyperLogLog.
    pub fn clear(&mut self) {
        self.registers = vec![0; self.registers.len()];
    }

    /// Checks whether the HyperLogLog has never seen an element.
    pub fn is_empty(&self) -> bool {
        self.registers.iter().all(|&x| x == 0)
    }
}

impl fmt::Debug for HyperLogLog {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HyperLogLog {{ b: {} }}", self.b)
    }
}

#[cfg(test)]
mod tests {
    use super::HyperLogLog;

    #[test]
    fn getter() {
        let hll = HyperLogLog::new(8);
        assert_eq!(hll.get_b(), 8);
        assert_eq!(hll.get_m(), 1 << 8);
    }

    #[test]
    fn empty() {
        let hll = HyperLogLog::new(8);
        assert_eq!(hll.count(), 0);
        assert!(hll.is_empty());
    }

    #[test]
    fn add_b4_n1k() {
        let mut hll = HyperLogLog::new(4);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 571);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b8_n1k() {
        let mut hll = HyperLogLog::new(8);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 966);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b12_n1k() {
        let mut hll = HyperLogLog::new(12);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 984);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n1k() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..1000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 998);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b8_n10k() {
        let mut hll = HyperLogLog::new(8);
        for i in 0..10000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 10196);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b12_n10k() {
        let mut hll = HyperLogLog::new(12);
        for i in 0..10000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 10303);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n10k() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..10000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 10055);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n100k() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..100000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 100551);
        assert!(!hll.is_empty());
    }

    #[test]
    fn add_b16_n1m() {
        let mut hll = HyperLogLog::new(16);
        for i in 0..1000000 {
            hll.add(&i);
        }
        assert_eq!(hll.count(), 1000226);
        assert!(!hll.is_empty());
    }

    #[test]
    fn clear() {
        let mut hll = HyperLogLog::new(8);
        for i in 0..1000 {
            hll.add(&i);
        }
        hll.clear();
        assert_eq!(hll.count(), 0);
        assert!(hll.is_empty());
    }

    #[test]
    fn clone() {
        let mut hll1 = HyperLogLog::new(12);
        for i in 0..500 {
            hll1.add(&i);
        }
        let c1a = hll1.count();

        let hll2 = hll1.clone();
        assert_eq!(hll2.count(), c1a);

        for i in 501..1000 {
            hll1.add(&i);
        }
        let c1b = hll1.count();
        assert_ne!(c1b, c1a);
        assert_eq!(hll2.count(), c1a);
    }

    #[test]
    fn merge() {
        let mut hll1 = HyperLogLog::new(12);
        let mut hll2 = HyperLogLog::new(12);
        let mut hll = HyperLogLog::new(12);
        for i in 0..500 {
            hll.add(&i);
            hll1.add(&i);
        }
        for i in 501..1000 {
            hll.add(&i);
            hll2.add(&i);
        }
        assert_ne!(hll.count(), hll1.count());
        assert_ne!(hll.count(), hll2.count());

        hll1.merge(&hll2);
        assert_eq!(hll.count(), hll1.count());
    }

    #[test]
    fn debug() {
        let hll = HyperLogLog::new(12);
        assert_eq!(format!("{:?}", hll), "HyperLogLog { b: 12 }");
    }
}
<|endoftext|>


<commit_before><commit_msg>windows: Use XMM4 for FREG_TMP1<commit_after><|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core_foundation::base::{AbstractCFTypeRef, CFTypeID, CFTypeRef, CFWrapper};

use data_provider::{CGDataProvider, CGDataProviderRef};

pub type CGGlyph = libc::c_ushort;

struct __CGFont { private: () }
pub type CGFontRef = *__CGFont;

impl AbstractCFTypeRef for CGFontRef {
    fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }

    fn type_id() -> CFTypeID {
        unsafe {
            CGFontGetTypeID()
        }
    }
}

pub struct CGFont {
    contents: CFWrapper<CGFontRef, (), ()>
}

impl CGFont {
    /// Convenience method to make it easier to wrap external `CGFont` instances.
    pub fn wrap_shared(font: CGFontRef) -> CGFont {
        CGFont {
            contents: CFWrapper::wrap_shared(font)
        }
    }
}

impl Clone for CGFont {
    fn clone(&self) -> CGFont {
        CGFont::wrap_shared(*self.contents.borrow_ref())
    }
}

pub fn create_with_data_provider(provider: &CGDataProvider) -> CGFont {
    // TODO: error handling
    unsafe {
        let value = CGFontCreateWithDataProvider(*provider.borrow_ref());
        CGFont {
            contents: CFWrapper::wrap_owned(value)
        }
    }
}

#[nolink]
#[link_args="-framework ApplicationServices"]
extern {

    // TODO: basically nothing has bindings (even commented-out)  besides what we use.
    fn CGFontCreateWithDataProvider(provider: CGDataProviderRef) -> CGFontRef;
    fn CGFontGetTypeID() -> CFTypeID;

    // These do the same thing as CFRetain/CFRelease, except
    // gracefully handle a NULL argument. We don't use them.
    fn CGFontRetain(font: CGFontRef);
    fn CGFontRelease(font: CGFontRef);
}
<commit_msg>Add CGFont::wrap_owned<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core_foundation::base::{AbstractCFTypeRef, CFTypeID, CFTypeRef, CFWrapper};

use data_provider::{CGDataProvider, CGDataProviderRef};

pub type CGGlyph = libc::c_ushort;

struct __CGFont { private: () }
pub type CGFontRef = *__CGFont;

impl AbstractCFTypeRef for CGFontRef {
    fn as_type_ref(&self) -> CFTypeRef { *self as CFTypeRef }

    fn type_id() -> CFTypeID {
        unsafe {
            CGFontGetTypeID()
        }
    }
}

pub struct CGFont {
    contents: CFWrapper<CGFontRef, (), ()>
}

impl CGFont {
    /// Convenience method to make it easier to wrap external `CGFont` instances.
    pub fn wrap_owned(font: CGFontRef) -> CGFont {
        CGFont {
            contents: CFWrapper::wrap_owned(font)
        }
    }

    /// Convenience method to make it easier to wrap external `CGFont` instances.
    pub fn wrap_shared(font: CGFontRef) -> CGFont {
        CGFont {
            contents: CFWrapper::wrap_shared(font)
        }
    }
}

impl Clone for CGFont {
    fn clone(&self) -> CGFont {
        CGFont::wrap_shared(*self.contents.borrow_ref())
    }
}

pub fn create_with_data_provider(provider: &CGDataProvider) -> CGFont {
    // TODO: error handling
    unsafe {
        let value = CGFontCreateWithDataProvider(*provider.borrow_ref());
        CGFont {
            contents: CFWrapper::wrap_owned(value)
        }
    }
}

#[nolink]
#[link_args="-framework ApplicationServices"]
extern {

    // TODO: basically nothing has bindings (even commented-out)  besides what we use.
    fn CGFontCreateWithDataProvider(provider: CGDataProviderRef) -> CGFontRef;
    fn CGFontGetTypeID() -> CFTypeID;

    // These do the same thing as CFRetain/CFRelease, except
    // gracefully handle a NULL argument. We don't use them.
    fn CGFontRetain(font: CGFontRef);
    fn CGFontRelease(font: CGFontRef);
}
<|endoftext|>


<commit_before>//! Infrastructure for compiler plugins.
//!
//! Plugins are a deprecated way to extend the behavior of `rustc` in various ways.
//!
//! See the [`plugin`
//! feature](https://doc.rust-lang.org/nightly/unstable-book/language-features/plugin.html)
//! of the Unstable Book for some examples.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(nll)]

use rustc_lint::LintStore;

pub mod build;
pub mod load;

/// Structure used to register plugins.
///
/// A plugin registrar function takes an `&mut Registry` and should call
/// methods to register its plugins.
pub struct Registry<'a> {
    /// The `LintStore` allows plugins to register new lints.
    pub lint_store: &'a mut LintStore,
}
<commit_msg>Increase `recursion_limit` in `librustc_plugin_impl`.<commit_after>//! Infrastructure for compiler plugins.
//!
//! Plugins are a deprecated way to extend the behavior of `rustc` in various ways.
//!
//! See the [`plugin`
//! feature](https://doc.rust-lang.org/nightly/unstable-book/language-features/plugin.html)
//! of the Unstable Book for some examples.

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(nll)]
#![recursion_limit = "256"]

use rustc_lint::LintStore;

pub mod build;
pub mod load;

/// Structure used to register plugins.
///
/// A plugin registrar function takes an `&mut Registry` and should call
/// methods to register its plugins.
pub struct Registry<'a> {
    /// The `LintStore` allows plugins to register new lints.
    pub lint_store: &'a mut LintStore,
}
<|endoftext|>


<commit_before>use std::ffi::{CStr, CString};
use std::path::Path;

use ll::*;
use tcl::{TclResult, TclEnvironment};
use object::Object;

/// Which scope to evaluate a command in
pub enum EvalScope {
    /// Evaluate a command at the current scope
    Local = 0,
    /// Evaluate a command at the highest-possible scope
    Global = TCL_EVAL_GLOBAL as isize,
}

/// Should we byte compile a command
pub enum ByteCompile {
    /// Compile the command into bytecode
    Compile = 0,
    /// Do not compile the command into bytecode
    Direct = TCL_EVAL_DIRECT as isize
}

/// An instance of a Tcl interpreter
pub struct Interpreter <'env> {
    _env: &'env TclEnvironment,
    raw: *mut Tcl_Interp
}

#[unsafe_destructor]
impl <'env> Drop for Interpreter <'env> {
    fn drop(&mut self) {
        unsafe { Tcl_DeleteInterp(self.raw) };
    }
}

impl <'env> Interpreter <'env> {

    /// Create a new Interpreter
    pub fn new(env: &TclEnvironment) -> Interpreter {
        Interpreter {
            _env: env,
            raw: unsafe { Tcl_CreateInterp() }
        }
    }

    pub unsafe fn raw(&mut self) -> *mut Tcl_Interp {
        self.raw
    }

    //TODO: Child interpreters - create, get, get parent, paths

    /// Check whether the interpreter has been marked as safe
    pub fn is_safe(&self) -> bool {
        unsafe { Tcl_IsSafe(self.raw) == 1 }
    }

    /// Disable 'unsafe' commands and variables in the interpreter
    pub fn make_safe(&mut self) -> TclResult {
        let result = unsafe { Tcl_MakeSafe(self.raw) };
        TclResult::from_ll(result, self)
    }

    /// Get the string result of the last run command
    pub fn string_result(&self) -> String {
        unsafe {
            let string = Tcl_GetStringResult(self.raw);
            String::from_utf8_lossy(CStr::from_ptr(string).to_bytes()).to_string()
        }
    }

    /// Get a native Tcl object from the last run command
    pub fn object_result(&self) -> Object<'env> {
        unsafe {
            let object = Tcl_GetObjResult(self.raw);
            Object::from_raw(self._env, object)
        }
    }

    /// Evaluate an external file of Tcl code, and store the result internally
    pub fn eval_file(&mut self, path: &Path) -> TclResult {
        let buf = CString::new(path.to_string_lossy().as_bytes()).unwrap().as_ptr();
        let result = unsafe {
            Tcl_EvalFile(self.raw, buf)
        };
        TclResult::from_ll(result, self)
    }

    /// Evaluate a string of Tcl code, and store the result internally
    pub fn eval(&mut self, code: &str, eval_scope: EvalScope) -> TclResult {
        let buf = CString::new(code.as_bytes()).unwrap().as_ptr();

        let flags = eval_scope as i32;

        let result = unsafe {
            Tcl_EvalEx(self.raw, buf, code.len() as i32, flags)
        };
        TclResult::from_ll(result, self)
    }

    /// Evaluate a Tcl objecrt as code, and store the result internally
    pub fn eval_object(&mut self, code: &Object, eval_scope: EvalScope, byte_compile: ByteCompile) -> TclResult {
        let flags = (eval_scope as i32) & (byte_compile as i32);
        let result = unsafe {
            Tcl_EvalObjEx(self.raw, code.raw(), flags)
        };
        TclResult::from_ll(result, self)
    }

    /// Attempt to extract a boolean from a Tcl value
    pub fn get_boolean_from_object(&mut self, obj: &Object) -> Result<bool, String> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetBooleanFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output != 0)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Attempt to extract an integer from a Tcl value
    pub fn get_integer_from_object(&mut self, obj: &Object) -> Result<i32, String> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetIntFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Attempt to extract a long from a Tcl value
    pub fn get_long_from_object(&mut self, obj: &Object) -> Result<i64, String> {
        let mut output = 0i64;
        unsafe {
            if Tcl_GetLongFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    //TODO: WideInt
    //TODO: BigNum

    /// Attempt to extract a double from a Tcl value
    pub fn get_double_from_object(&mut self, obj: &Object) -> Result<f64, String> {
        let mut output = 0f64;
        unsafe {
            if Tcl_GetDoubleFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Append an element to a list
    /// This will fail if target is shared
    pub fn list_append(&mut self, target: &mut Object, source: &Object) -> TclResult {
        let result = unsafe {
            Tcl_ListObjAppendElement(self.raw, target.raw(), source.raw())
        };
        TclResult::from_ll(result, self)
    }
}
<commit_msg>Untested Expression commands<commit_after>use std::ffi::{CStr, CString};
use std::path::Path;
use std::ptr;

use ll::*;
use tcl::{TclResult, TclEnvironment};
use object::Object;

/// Which scope to evaluate a command in
pub enum EvalScope {
    /// Evaluate a command at the current scope
    Local = 0,
    /// Evaluate a command at the highest-possible scope
    Global = TCL_EVAL_GLOBAL as isize,
}

/// Should we byte compile a command
pub enum ByteCompile {
    /// Compile the command into bytecode
    Compile = 0,
    /// Do not compile the command into bytecode
    Direct = TCL_EVAL_DIRECT as isize
}

/// An instance of a Tcl interpreter
pub struct Interpreter <'env> {
    _env: &'env TclEnvironment,
    raw: *mut Tcl_Interp
}

#[unsafe_destructor]
impl <'env> Drop for Interpreter <'env> {
    fn drop(&mut self) {
        unsafe { Tcl_DeleteInterp(self.raw) };
    }
}

impl <'env> Interpreter <'env> {

    /// Create a new Interpreter
    pub fn new(env: &TclEnvironment) -> Interpreter {
        Interpreter {
            _env: env,
            raw: unsafe { Tcl_CreateInterp() }
        }
    }

    pub unsafe fn raw(&mut self) -> *mut Tcl_Interp {
        self.raw
    }

    //TODO: Child interpreters - create, get, get parent, paths

    /// Check whether the interpreter has been marked as safe
    pub fn is_safe(&self) -> bool {
        unsafe { Tcl_IsSafe(self.raw) == 1 }
    }

    /// Disable 'unsafe' commands and variables in the interpreter
    pub fn make_safe(&mut self) -> TclResult {
        let result = unsafe { Tcl_MakeSafe(self.raw) };
        TclResult::from_ll(result, self)
    }

    /// Get the string result of the last run command
    pub fn string_result(&self) -> String {
        unsafe {
            let string = Tcl_GetStringResult(self.raw);
            String::from_utf8_lossy(CStr::from_ptr(string).to_bytes()).to_string()
        }
    }

    /// Get a native Tcl object from the last run command
    pub fn object_result(&self) -> Object<'env> {
        unsafe {
            let object = Tcl_GetObjResult(self.raw);
            Object::from_raw(self._env, object)
        }
    }

    /// Evaluate an external file of Tcl code, and store the result internally
    pub fn eval_file(&mut self, path: &Path) -> TclResult {
        let buf = CString::new(path.to_string_lossy().as_bytes()).unwrap().as_ptr();
        let result = unsafe {
            Tcl_EvalFile(self.raw, buf)
        };
        TclResult::from_ll(result, self)
    }

    /// Evaluate a string of Tcl code, and store the result internally
    pub fn eval(&mut self, code: &str, eval_scope: EvalScope) -> TclResult {
        let buf = CString::new(code.as_bytes()).unwrap().as_ptr();

        let flags = eval_scope as i32;

        let result = unsafe {
            Tcl_EvalEx(self.raw, buf, code.len() as i32, flags)
        };
        TclResult::from_ll(result, self)
    }

    /// Evaluate a Tcl objecrt as code, and store the result internally
    pub fn eval_object(&mut self, code: &Object, eval_scope: EvalScope, byte_compile: ByteCompile) -> TclResult {
        let flags = (eval_scope as i32) & (byte_compile as i32);
        let result = unsafe {
            Tcl_EvalObjEx(self.raw, code.raw(), flags)
        };
        TclResult::from_ll(result, self)
    }

    /// Attempt to extract a boolean from a Tcl value
    pub fn get_boolean_from_object(&mut self, obj: &Object) -> Result<bool, String> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetBooleanFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output != 0)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Attempt to extract an integer from a Tcl value
    pub fn get_integer_from_object(&mut self, obj: &Object) -> Result<i32, String> {
        let mut output = 0i32;
        unsafe {
            if Tcl_GetIntFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Attempt to extract a long from a Tcl value
    pub fn get_long_from_object(&mut self, obj: &Object) -> Result<i64, String> {
        let mut output = 0i64;
        unsafe {
            if Tcl_GetLongFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    //TODO: WideInt
    //TODO: BigNum

    /// Attempt to extract a double from a Tcl value
    pub fn get_double_from_object(&mut self, obj: &Object) -> Result<f64, String> {
        let mut output = 0f64;
        unsafe {
            if Tcl_GetDoubleFromObj(self.raw, obj.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Append an element to a list
    /// This will fail if target is shared
    pub fn list_append(&mut self, target: &mut Object, source: &Object) -> TclResult {
        let result = unsafe {
            Tcl_ListObjAppendElement(self.raw, target.raw(), source.raw())
        };
        TclResult::from_ll(result, self)
    }

    /// Get the boolean result of an expression
    pub fn expression_boolean(&mut self, expr: &str) -> Result<bool, String> {
        let mut output = 0;
        let buf = CString::new(expr.as_bytes()).unwrap().as_ptr();
        unsafe {
            if Tcl_ExprBoolean(self.raw, buf, &mut output) == 0 {
                Ok(output == 1)
            } else {
                Err(self.string_result())
            }
        }
    }

    // Get the boolean result of an expression object
    pub fn expression_boolean_from_object(&mut self, expr: &Object) -> Result<bool, String> {
        let mut output = 0;
        unsafe {
            if Tcl_ExprBooleanObj(self.raw, expr.raw(), &mut output) == 0 {
                Ok(output == 1)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Get the double result of an expression
    pub fn expression_double(&mut self, expr: &str) -> Result<f64, String> {
        let mut output = 0.0;
        let buf = CString::new(expr.as_bytes()).unwrap().as_ptr();
        unsafe {
            if Tcl_ExprDouble(self.raw, buf, &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    // Get the double result of an expression object
    pub fn expression_double_from_object(&mut self, expr: &Object) -> Result<f64, String> {
        let mut output = 0.0;
        unsafe {
            if Tcl_ExprDoubleObj(self.raw, expr.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Get the long result of an expression
    pub fn expression_long(&mut self, expr: &str) -> Result<i64, String> {
        let mut output = 0;
        let buf = CString::new(expr.as_bytes()).unwrap().as_ptr();
        unsafe {
            if Tcl_ExprLong(self.raw, buf, &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    // Get the long result of an expression object
    pub fn expression_long_from_object(&mut self, expr: &Object) -> Result<i64, String> {
        let mut output = 0;
        unsafe {
            if Tcl_ExprLongObj(self.raw, expr.raw(), &mut output) == 0 {
                Ok(output)
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Get the object result of an expression object
    pub fn expression_object_from_object(&mut self, expr: &Object) -> Result<Object<'env>, String> {
        let mut output = ptr::null_mut();
        unsafe {
            if Tcl_ExprObj(self.raw, expr.raw(), &mut output) == 0 {
                Ok(Object::from_raw(self._env, output))
            } else {
                Err(self.string_result())
            }
        }
    }

    /// Process an expression string and store the result
    pub fn expression_string(&mut self, expr: &str) -> TclResult {
        let buf = CString::new(expr.as_bytes()).unwrap().as_ptr();
        let result = unsafe {
            Tcl_ExprString(self.raw, buf)
        };
        TclResult::from_ll(result, self)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(warnings)]

extern crate build_helper;
extern crate gcc;

use std::env;
use std::path::PathBuf;
use std::process::Command;
use build_helper::{run, native_lib_boilerplate};

fn main() {
    // FIXME: This is a hack to support building targets that don't
    // support jemalloc alongside hosts that do. The jemalloc build is
    // controlled by a feature of the std crate, and if that feature
    // changes between targets, it invalidates the fingerprint of
    // std's build script (this is a cargo bug); so we must ensure
    // that the feature set used by std is the same across all
    // targets, which means we have to build the alloc_jemalloc crate
    // for targets like emscripten, even if we don't use it.
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    if target.contains("rumprun") || target.contains("bitrig") || target.contains("openbsd") ||
       target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") ||
       target.contains("redox") {
        println!("cargo:rustc-cfg=dummy_jemalloc");
        return;
    }

    if target.contains("android") {
        println!("cargo:rustc-link-lib=gcc");
    } else if !target.contains("windows") && !target.contains("musl") {
        println!("cargo:rustc-link-lib=pthread");
    }

    if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
        let jemalloc = PathBuf::from(jemalloc);
        println!("cargo:rustc-link-search=native={}",
                 jemalloc.parent().unwrap().display());
        let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
        let name = jemalloc.file_name().unwrap().to_str().unwrap();
        let kind = if name.ends_with(".a") {
            "static"
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
        return;
    }

    let link_name = if target.contains("windows") { "jemalloc" } else { "jemalloc_pic" };
    let native = match native_lib_boilerplate("jemalloc", "jemalloc", link_name, "lib") {
        Ok(native) => native,
        _ => return,
    };

    let compiler = gcc::Config::new().get_compiler();
    // only msvc returns None for ar so unwrap is okay
    let ar = build_helper::cc2ar(compiler.path(), &target).unwrap();
    let cflags = compiler.args()
        .iter()
        .map(|s| s.to_str().unwrap())
        .collect::<Vec<_>>()
        .join(" ");

    let mut cmd = Command::new("sh");
    cmd.arg(native.src_dir.join("configure")
                          .to_str()
                          .unwrap()
                          .replace("C:\\", "/c/")
                          .replace("\\", "/"))
       .current_dir(&native.out_dir)
       .env("CC", compiler.path())
       .env("EXTRA_CFLAGS", cflags.clone())
       // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
       // that GCC will run the preprocessor, and only the preprocessor, over
       // jemalloc's source files. If we don't specify CPPFLAGS, then at least
       // on ARM that step fails with a "Missing implementation for 32-bit
       // atomic operations" error. This is because no "-march" flag will be
       // passed to GCC, and then GCC won't define the
       // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
       // select an atomic operation implementation.
       .env("CPPFLAGS", cflags.clone())
       .env("AR", &ar)
       .env("RANLIB", format!("{} s", ar.display()));

    if target.contains("windows") {
        // A bit of history here, this used to be --enable-lazy-lock added in
        // #14006 which was filed with jemalloc in jemalloc/jemalloc#83 which
        // was also reported to MinGW:
        //
        //  http://sourceforge.net/p/mingw-w64/bugs/395/
        //
        // When updating jemalloc to 4.0, however, it was found that binaries
        // would exit with the status code STATUS_RESOURCE_NOT_OWNED indicating
        // that a thread was unlocking a mutex it never locked. Disabling this
        // "lazy lock" option seems to fix the issue, but it was enabled by
        // default for MinGW targets in 13473c7 for jemalloc.
        //
        // As a result of all that, force disabling lazy lock on Windows, and
        // after reading some code it at least *appears* that the initialization
        // of mutexes is otherwise ok in jemalloc, so shouldn't cause problems
        // hopefully...
        //
        // tl;dr: make windows behave like other platforms by disabling lazy
        //        locking, but requires passing an option due to a historical
        //        default with jemalloc.
        cmd.arg("--disable-lazy-lock");
    } else if target.contains("ios") {
        cmd.arg("--disable-tls");
    } else if target.contains("android") {
        // We force android to have prefixed symbols because apparently
        // replacement of the libc allocator doesn't quite work. When this was
        // tested (unprefixed symbols), it was found that the `realpath`
        // function in libc would allocate with libc malloc (not jemalloc
        // malloc), and then the standard library would free with jemalloc free,
        // causing a segfault.
        //
        // If the test suite passes, however, without symbol prefixes then we
        // should be good to go!
        cmd.arg("--with-jemalloc-prefix=je_");
        cmd.arg("--disable-tls");
    } else if target.contains("dragonfly") || target.contains("musl") {
        cmd.arg("--with-jemalloc-prefix=je_");
    }

    if cfg!(feature = "debug-jemalloc") {
        cmd.arg("--enable-debug");
    }

    // Turn off broken quarantine (see jemalloc/jemalloc#161)
    cmd.arg("--disable-fill");
    cmd.arg(format!("--host={}", build_helper::gnu_target(&target)));
    cmd.arg(format!("--build={}", build_helper::gnu_target(&host)));

    // for some reason, jemalloc configure doesn't detect this value
    // automatically for this target
    if target == "sparc64-unknown-linux-gnu" {
        cmd.arg("--with-lg-quantum=4");
    }

    run(&mut cmd);

    let mut make = Command::new(build_helper::make(&host));
    make.current_dir(&native.out_dir)
        .arg("build_lib_static");

    // These are intended for mingw32-make which we don't use
    if cfg!(windows) {
        make.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    }

    // mingw make seems... buggy? unclear...
    if !host.contains("windows") {
        make.arg("-j")
            .arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set"));
    }

    run(&mut make);

    // The pthread_atfork symbols is used by jemalloc on android but the really
    // old android we're building on doesn't have them defined, so just make
    // sure the symbols are available.
    if target.contains("androideabi") {
        println!("cargo:rerun-if-changed=pthread_atfork_dummy.c");
        gcc::Config::new()
            .flag("-fvisibility=hidden")
            .file("pthread_atfork_dummy.c")
            .compile("libpthread_atfork_dummy.a");
    }
}
<commit_msg>Stop disabling fill in jemalloc<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![deny(warnings)]

extern crate build_helper;
extern crate gcc;

use std::env;
use std::path::PathBuf;
use std::process::Command;
use build_helper::{run, native_lib_boilerplate};

fn main() {
    // FIXME: This is a hack to support building targets that don't
    // support jemalloc alongside hosts that do. The jemalloc build is
    // controlled by a feature of the std crate, and if that feature
    // changes between targets, it invalidates the fingerprint of
    // std's build script (this is a cargo bug); so we must ensure
    // that the feature set used by std is the same across all
    // targets, which means we have to build the alloc_jemalloc crate
    // for targets like emscripten, even if we don't use it.
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    if target.contains("rumprun") || target.contains("bitrig") || target.contains("openbsd") ||
       target.contains("msvc") || target.contains("emscripten") || target.contains("fuchsia") ||
       target.contains("redox") {
        println!("cargo:rustc-cfg=dummy_jemalloc");
        return;
    }

    if target.contains("android") {
        println!("cargo:rustc-link-lib=gcc");
    } else if !target.contains("windows") && !target.contains("musl") {
        println!("cargo:rustc-link-lib=pthread");
    }

    if let Some(jemalloc) = env::var_os("JEMALLOC_OVERRIDE") {
        let jemalloc = PathBuf::from(jemalloc);
        println!("cargo:rustc-link-search=native={}",
                 jemalloc.parent().unwrap().display());
        let stem = jemalloc.file_stem().unwrap().to_str().unwrap();
        let name = jemalloc.file_name().unwrap().to_str().unwrap();
        let kind = if name.ends_with(".a") {
            "static"
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, &stem[3..]);
        return;
    }

    let link_name = if target.contains("windows") { "jemalloc" } else { "jemalloc_pic" };
    let native = match native_lib_boilerplate("jemalloc", "jemalloc", link_name, "lib") {
        Ok(native) => native,
        _ => return,
    };

    let compiler = gcc::Config::new().get_compiler();
    // only msvc returns None for ar so unwrap is okay
    let ar = build_helper::cc2ar(compiler.path(), &target).unwrap();
    let cflags = compiler.args()
        .iter()
        .map(|s| s.to_str().unwrap())
        .collect::<Vec<_>>()
        .join(" ");

    let mut cmd = Command::new("sh");
    cmd.arg(native.src_dir.join("configure")
                          .to_str()
                          .unwrap()
                          .replace("C:\\", "/c/")
                          .replace("\\", "/"))
       .current_dir(&native.out_dir)
       .env("CC", compiler.path())
       .env("EXTRA_CFLAGS", cflags.clone())
       // jemalloc generates Makefile deps using GCC's "-MM" flag. This means
       // that GCC will run the preprocessor, and only the preprocessor, over
       // jemalloc's source files. If we don't specify CPPFLAGS, then at least
       // on ARM that step fails with a "Missing implementation for 32-bit
       // atomic operations" error. This is because no "-march" flag will be
       // passed to GCC, and then GCC won't define the
       // "__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4" macro that jemalloc needs to
       // select an atomic operation implementation.
       .env("CPPFLAGS", cflags.clone())
       .env("AR", &ar)
       .env("RANLIB", format!("{} s", ar.display()));

    if target.contains("windows") {
        // A bit of history here, this used to be --enable-lazy-lock added in
        // #14006 which was filed with jemalloc in jemalloc/jemalloc#83 which
        // was also reported to MinGW:
        //
        //  http://sourceforge.net/p/mingw-w64/bugs/395/
        //
        // When updating jemalloc to 4.0, however, it was found that binaries
        // would exit with the status code STATUS_RESOURCE_NOT_OWNED indicating
        // that a thread was unlocking a mutex it never locked. Disabling this
        // "lazy lock" option seems to fix the issue, but it was enabled by
        // default for MinGW targets in 13473c7 for jemalloc.
        //
        // As a result of all that, force disabling lazy lock on Windows, and
        // after reading some code it at least *appears* that the initialization
        // of mutexes is otherwise ok in jemalloc, so shouldn't cause problems
        // hopefully...
        //
        // tl;dr: make windows behave like other platforms by disabling lazy
        //        locking, but requires passing an option due to a historical
        //        default with jemalloc.
        cmd.arg("--disable-lazy-lock");
    } else if target.contains("ios") {
        cmd.arg("--disable-tls");
    } else if target.contains("android") {
        // We force android to have prefixed symbols because apparently
        // replacement of the libc allocator doesn't quite work. When this was
        // tested (unprefixed symbols), it was found that the `realpath`
        // function in libc would allocate with libc malloc (not jemalloc
        // malloc), and then the standard library would free with jemalloc free,
        // causing a segfault.
        //
        // If the test suite passes, however, without symbol prefixes then we
        // should be good to go!
        cmd.arg("--with-jemalloc-prefix=je_");
        cmd.arg("--disable-tls");
    } else if target.contains("dragonfly") || target.contains("musl") {
        cmd.arg("--with-jemalloc-prefix=je_");
    }

    if cfg!(feature = "debug-jemalloc") {
        cmd.arg("--enable-debug");
    }

    cmd.arg(format!("--host={}", build_helper::gnu_target(&target)));
    cmd.arg(format!("--build={}", build_helper::gnu_target(&host)));

    // for some reason, jemalloc configure doesn't detect this value
    // automatically for this target
    if target == "sparc64-unknown-linux-gnu" {
        cmd.arg("--with-lg-quantum=4");
    }

    run(&mut cmd);

    let mut make = Command::new(build_helper::make(&host));
    make.current_dir(&native.out_dir)
        .arg("build_lib_static");

    // These are intended for mingw32-make which we don't use
    if cfg!(windows) {
        make.env_remove("MAKEFLAGS").env_remove("MFLAGS");
    }

    // mingw make seems... buggy? unclear...
    if !host.contains("windows") {
        make.arg("-j")
            .arg(env::var("NUM_JOBS").expect("NUM_JOBS was not set"));
    }

    run(&mut make);

    // The pthread_atfork symbols is used by jemalloc on android but the really
    // old android we're building on doesn't have them defined, so just make
    // sure the symbols are available.
    if target.contains("androideabi") {
        println!("cargo:rerun-if-changed=pthread_atfork_dummy.c");
        gcc::Config::new()
            .flag("-fvisibility=hidden")
            .file("pthread_atfork_dummy.c")
            .compile("libpthread_atfork_dummy.a");
    }
}
<|endoftext|>