<commit_before>use std::fmt;
use std::str::FromStr;
use header::{Header, HeaderFormat};
use super::util::{from_one_comma_delimited, fmt_comma_delimited};

/// The Cache-Control header.
#[deriving(PartialEq, Clone, Show)]
pub struct CacheControl(pub Vec<CacheDirective>);

deref!(CacheControl -> Vec<CacheDirective>);

impl Header for CacheControl {
    fn header_name(_: Option<CacheControl>) -> &'static str {
        "Cache-Control"
    }

    fn parse_header(raw: &[Vec<u8>]) -> Option<CacheControl> {
        let directives = raw.iter()
            .filter_map(|line| from_one_comma_delimited(line[]))
            .collect::<Vec<Vec<CacheDirective>>>()
            .concat_vec();
        if directives.len() > 0 {
            Some(CacheControl(directives))
        } else {
            None
        }
    }
}

impl HeaderFormat for CacheControl {
    fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt_comma_delimited(fmt, self[])
    }
}

/// CacheControl contains a list of these directives.
#[deriving(PartialEq, Clone)]
pub enum CacheDirective {
    /// "no-cache"
    NoCache,
    /// "no-store"
    NoStore,
    /// "no-transform"
    NoTransform,
    /// "only-if-cached"
    OnlyIfCached,

    // request directives
    /// "max-age=delta"
    MaxAge(uint),
    /// "max-stale=delta"
    MaxStale(uint),
    /// "min-fresh=delta"
    MinFresh(uint),

    // response directives
    /// "must-revalidate"
    MustRevalidate,
    /// "public"
    Public,
    /// "private"
    Private,
    /// "proxy-revalidate"
    ProxyRevalidate,
    /// "s-maxage=delta"
    SMaxAge(uint),

    /// Extension directives. Optionally include an argument.
    Extension(String, Option<String>)
}

impl fmt::Show for CacheDirective {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::CacheDirective::*;
        match *self {
            NoCache => "no-cache",
            NoStore => "no-store",
            NoTransform => "no-transform",
            OnlyIfCached => "only-if-cached",

            MaxAge(secs) => return write!(f, "max-age={}", secs),
            MaxStale(secs) => return write!(f, "max-stale={}", secs),
            MinFresh(secs) => return write!(f, "min-fresh={}", secs),

            MustRevalidate => "must-revalidate",
            Public => "public",
            Private => "private",
            ProxyRevalidate => "proxy-revalidate",
            SMaxAge(secs) => return write!(f, "s-maxage={}", secs),

            Extension(ref name, None) => name[],
            Extension(ref name, Some(ref arg)) => return write!(f, "{}={}", name, arg),

        }.fmt(f)
    }
}

impl FromStr for CacheDirective {
    fn from_str(s: &str) -> Option<CacheDirective> {
        use self::CacheDirective::*;
        match s {
            "no-cache" => Some(NoCache),
            "no-store" => Some(NoStore),
            "no-transform" => Some(NoTransform),
            "only-if-cached" => Some(OnlyIfCached),
            "must-revalidate" => Some(MustRevalidate),
            "public" => Some(Public),
            "private" => Some(Private),
            "proxy-revalidate" => Some(ProxyRevalidate),
            "" => None,
            _ => match s.find('=') {
                Some(idx) if idx+1 < s.len() => match (s[..idx], s[idx+1..].trim_chars('"')) {
                    ("max-age" , secs) => secs.parse().map(MaxAge),
                    ("max-stale", secs) => secs.parse().map(MaxStale),
                    ("min-fresh", secs) => secs.parse().map(MinFresh),
                    ("s-maxage", secs) => secs.parse().map(SMaxAge),
                    (left, right) => Some(Extension(left.to_string(), Some(right.to_string())))
                },
                Some(_) => None,
                None => Some(Extension(s.to_string(), None))
            }
        }
    }
}

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

    #[test]
    fn test_parse_multiple_headers() {
        let cache = Header::parse_header(&[b"no-cache".to_vec(), b"private".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::NoCache,
                                                 CacheDirective::Private])))
    }

    #[test]
    fn test_parse_argument() {
        let cache = Header::parse_header(&[b"max-age=100, private".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(100),
                                                 CacheDirective::Private])))
    }

    #[test]
    fn test_parse_quote_form() {
        let cache = Header::parse_header(&[b"max-age=\"200\"".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
    }

    #[test]
    fn test_parse_extension() {
        let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::Extension("foo".to_string(), None),
                                                 CacheDirective::Extension("bar".to_string(), Some("baz".to_string()))])))
    }

    #[test]
    fn test_parse_bad_syntax() {
        let cache: Option<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]);
        assert_eq!(cache, None)
    }
}

bench_header!(normal, CacheControl, { vec![b"no-cache, private".to_vec(), b"max-age=100".to_vec()] });

<commit_msg>Changed deprecated trim_chars to trim_matches<commit_after>use std::fmt;
use std::str::FromStr;
use header::{Header, HeaderFormat};
use super::util::{from_one_comma_delimited, fmt_comma_delimited};

/// The Cache-Control header.
#[deriving(PartialEq, Clone, Show)]
pub struct CacheControl(pub Vec<CacheDirective>);

deref!(CacheControl -> Vec<CacheDirective>);

impl Header for CacheControl {
    fn header_name(_: Option<CacheControl>) -> &'static str {
        "Cache-Control"
    }

    fn parse_header(raw: &[Vec<u8>]) -> Option<CacheControl> {
        let directives = raw.iter()
            .filter_map(|line| from_one_comma_delimited(line[]))
            .collect::<Vec<Vec<CacheDirective>>>()
            .concat_vec();
        if directives.len() > 0 {
            Some(CacheControl(directives))
        } else {
            None
        }
    }
}

impl HeaderFormat for CacheControl {
    fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt_comma_delimited(fmt, self[])
    }
}

/// CacheControl contains a list of these directives.
#[deriving(PartialEq, Clone)]
pub enum CacheDirective {
    /// "no-cache"
    NoCache,
    /// "no-store"
    NoStore,
    /// "no-transform"
    NoTransform,
    /// "only-if-cached"
    OnlyIfCached,

    // request directives
    /// "max-age=delta"
    MaxAge(uint),
    /// "max-stale=delta"
    MaxStale(uint),
    /// "min-fresh=delta"
    MinFresh(uint),

    // response directives
    /// "must-revalidate"
    MustRevalidate,
    /// "public"
    Public,
    /// "private"
    Private,
    /// "proxy-revalidate"
    ProxyRevalidate,
    /// "s-maxage=delta"
    SMaxAge(uint),

    /// Extension directives. Optionally include an argument.
    Extension(String, Option<String>)
}

impl fmt::Show for CacheDirective {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::CacheDirective::*;
        match *self {
            NoCache => "no-cache",
            NoStore => "no-store",
            NoTransform => "no-transform",
            OnlyIfCached => "only-if-cached",

            MaxAge(secs) => return write!(f, "max-age={}", secs),
            MaxStale(secs) => return write!(f, "max-stale={}", secs),
            MinFresh(secs) => return write!(f, "min-fresh={}", secs),

            MustRevalidate => "must-revalidate",
            Public => "public",
            Private => "private",
            ProxyRevalidate => "proxy-revalidate",
            SMaxAge(secs) => return write!(f, "s-maxage={}", secs),

            Extension(ref name, None) => name[],
            Extension(ref name, Some(ref arg)) => return write!(f, "{}={}", name, arg),

        }.fmt(f)
    }
}

impl FromStr for CacheDirective {
    fn from_str(s: &str) -> Option<CacheDirective> {
        use self::CacheDirective::*;
        match s {
            "no-cache" => Some(NoCache),
            "no-store" => Some(NoStore),
            "no-transform" => Some(NoTransform),
            "only-if-cached" => Some(OnlyIfCached),
            "must-revalidate" => Some(MustRevalidate),
            "public" => Some(Public),
            "private" => Some(Private),
            "proxy-revalidate" => Some(ProxyRevalidate),
            "" => None,
            _ => match s.find('=') {
                Some(idx) if idx+1 < s.len() => match (s[..idx], s[idx+1..].trim_matches('"')) {
                    ("max-age" , secs) => secs.parse().map(MaxAge),
                    ("max-stale", secs) => secs.parse().map(MaxStale),
                    ("min-fresh", secs) => secs.parse().map(MinFresh),
                    ("s-maxage", secs) => secs.parse().map(SMaxAge),
                    (left, right) => Some(Extension(left.to_string(), Some(right.to_string())))
                },
                Some(_) => None,
                None => Some(Extension(s.to_string(), None))
            }
        }
    }
}

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

    #[test]
    fn test_parse_multiple_headers() {
        let cache = Header::parse_header(&[b"no-cache".to_vec(), b"private".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::NoCache,
                                                 CacheDirective::Private])))
    }

    #[test]
    fn test_parse_argument() {
        let cache = Header::parse_header(&[b"max-age=100, private".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(100),
                                                 CacheDirective::Private])))
    }

    #[test]
    fn test_parse_quote_form() {
        let cache = Header::parse_header(&[b"max-age=\"200\"".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::MaxAge(200)])))
    }

    #[test]
    fn test_parse_extension() {
        let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]);
        assert_eq!(cache, Some(CacheControl(vec![CacheDirective::Extension("foo".to_string(), None),
                                                 CacheDirective::Extension("bar".to_string(), Some("baz".to_string()))])))
    }

    #[test]
    fn test_parse_bad_syntax() {
        let cache: Option<CacheControl> = Header::parse_header(&[b"foo=".to_vec()]);
        assert_eq!(cache, None)
    }
}

bench_header!(normal, CacheControl, { vec![b"no-cache, private".to_vec(), b"max-age=100".to_vec()] });

<|endoftext|>


<commit_before>// Copyright 2014 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.

#![allow(non_snake_case)]

register_long_diagnostics! {

E0046: r##"
When trying to make some type implement a trait `Foo`, you must, at minimum,
provide implementations for all of `Foo`'s required methods (meaning the
methods that do not have default implementations), as well as any required
trait items like associated types or constants.
"##,

E0054: r##"
It is not allowed to cast to a bool. If you are trying to cast a numeric type
to a bool, you can compare it with zero instead:

```
let x = 5;

// Ok
let x_is_nonzero = x != 0;

// Not allowed, won't compile
let x_is_nonzero = x as bool;
```
"##,

E0062: r##"
This error indicates that during an attempt to build a struct or struct-like
enum variant, one of the fields was specified more than once. Each field should
be specified exactly one time.
"##,

E0063: r##"
This error indicates that during an attempt to build a struct or struct-like
enum variant, one of the fields was not provided. Each field should be specified
exactly once.
"##,

E0067: r##"
The left-hand side of an assignment operator must be an lvalue expression. An
lvalue expression represents a memory location and includes item paths (ie,
namespaced variables), dereferences, indexing expressions, and field references.

```
use std::collections::LinkedList;

// Good
let mut list = LinkedList::new();

// Bad: assignment to non-lvalue expression
LinkedList::new() += 1;
```
"##,

E0081: r##"
Enum discriminants are used to differentiate enum variants stored in memory.
This error indicates that the same value was used for two or more variants,
making them impossible to tell apart.

```
// Good.
enum Enum {
    P,
    X = 3,
    Y = 5
}

// Bad.
enum Enum {
    P = 3,
    X = 3,
    Y = 5
}
```

Note that variants without a manually specified discriminant are numbered from
top to bottom starting from 0, so clashes can occur with seemingly unrelated
variants.

```
enum Bad {
    X,
    Y = 0
}
```

Here `X` will have already been assigned the discriminant 0 by the time `Y` is
encountered, so a conflict occurs.
"##,

E0082: r##"
The default type for enum discriminants is `isize`, but it can be adjusted by
adding the `repr` attribute to the enum declaration. This error indicates that
an integer literal given as a discriminant is not a member of the discriminant
type. For example:

```
#[repr(u8)]
enum Thing {
    A = 1024,
    B = 5
}
```

Here, 1024 lies outside the valid range for `u8`, so the discriminant for `A` is
invalid. You may want to change representation types to fix this, or else change
invalid discriminant values so that they fit within the existing type.

Note also that without a representation manually defined, the compiler will
optimize by using the smallest integer type possible.
"##,

E0083: r##"
At present, it's not possible to define a custom representation for an enum with
a single variant. As a workaround you can add a `Dummy` variant.

See: https://github.com/rust-lang/rust/issues/10292
"##,

E0084: r##"
It is impossible to define an integer type to be used to represent zero-variant
enum values because there are no zero-variant enum values. There is no way to
construct an instance of the following type using only safe code:

```
enum Empty {}
```
"##

}

register_diagnostics! {
    E0023,
    E0024,
    E0025,
    E0026,
    E0027,
    E0029,
    E0030,
    E0031,
    E0033,
    E0034,
    E0035,
    E0036,
    E0038,
    E0040, // explicit use of destructor method
    E0044,
    E0045,
    E0049,
    E0050,
    E0053,
    E0055,
    E0057,
    E0059,
    E0060,
    E0061,
    E0066,
    E0068,
    E0069,
    E0070,
    E0071,
    E0072,
    E0073,
    E0074,
    E0075,
    E0076,
    E0077,
    E0085,
    E0086,
    E0087,
    E0088,
    E0089,
    E0090,
    E0091,
    E0092,
    E0093,
    E0094,
    E0101,
    E0102,
    E0103,
    E0104,
    E0106,
    E0107,
    E0116,
    E0117,
    E0118,
    E0119,
    E0120,
    E0121,
    E0122,
    E0123,
    E0124,
    E0127,
    E0128,
    E0129,
    E0130,
    E0131,
    E0132,
    E0141,
    E0159,
    E0163,
    E0164,
    E0166,
    E0167,
    E0168,
    E0172,
    E0173, // manual implementations of unboxed closure traits are experimental
    E0174, // explicit use of unboxed closure methods are experimental
    E0178,
    E0182,
    E0183,
    E0184,
    E0185,
    E0186,
    E0187, // can't infer the kind of the closure
    E0188, // types differ in mutability
    E0189, // can only cast a boxed pointer to a boxed object
    E0190, // can only cast a &-pointer to an &-object
    E0191, // value of the associated type must be specified
    E0192, // negative imples are allowed just for `Send` and `Sync`
    E0193, // cannot bound type where clause bounds may only be attached to types
           // involving type parameters
    E0194,
    E0195, // lifetime parameters or bounds on method do not match the trait declaration
    E0196, // cannot determine a type for this closure
    E0197, // inherent impls cannot be declared as unsafe
    E0198, // negative implementations are not unsafe
    E0199, // implementing trait is not unsafe
    E0200, // trait requires an `unsafe impl` declaration
    E0201, // duplicate method in trait impl
    E0202, // associated items are not allowed in inherent impls
    E0203, // type parameter has more than one relaxed default bound,
           // and only one is supported
    E0204, // trait `Copy` may not be implemented for this type; field
           // does not implement `Copy`
    E0205, // trait `Copy` may not be implemented for this type; variant
           // does not implement `copy`
    E0206, // trait `Copy` may not be implemented for this type; type is
           // not a structure or enumeration
    E0207, // type parameter is not constrained by the impl trait, self type, or predicate
    E0208,
    E0209, // builtin traits can only be implemented on structs or enums
    E0210, // type parameter is not constrained by any local type
    E0211,
    E0212, // cannot extract an associated type from a higher-ranked trait bound
    E0213, // associated types are not accepted in this context
    E0214, // parenthesized parameters may only be used with a trait
    E0215, // angle-bracket notation is not stable with `Fn`
    E0216, // parenthetical notation is only stable with `Fn`
    E0217, // ambiguous associated type, defined in multiple supertraits
    E0218, // no associated type defined
    E0219, // associated type defined in higher-ranked supertrait
    E0220, // associated type not found for type parameter
    E0221, // ambiguous associated type in bounds
    E0222, // variadic function must have C calling convention
    E0223, // ambiguous associated type
    E0224, // at least one non-builtin train is required for an object type
    E0225, // only the builtin traits can be used as closure or object bounds
    E0226, // only a single explicit lifetime bound is permitted
    E0227, // ambiguous lifetime bound, explicit lifetime bound required
    E0228, // explicit lifetime bound required
    E0229, // associated type bindings are not allowed here
    E0230, // there is no type parameter on trait
    E0231, // only named substitution parameters are allowed
    E0232, // this attribute must have a value
    E0233,
    E0234, // `for` loop expression has type which does not implement the `Iterator` trait
    E0235, // structure constructor specifies a structure of type but
    E0236, // no lang item for range syntax
    E0237, // no lang item for range syntax
    E0238, // parenthesized parameters may only be used with a trait
    E0239, // `next` method of `Iterator` trait has unexpected type
    E0240,
    E0241,
    E0242, // internal error looking up a definition
    E0243, // wrong number of type arguments
    E0244, // wrong number of type arguments
    E0245, // not a trait
    E0246, // illegal recursive type
    E0247, // found module name used as a type
    E0248, // found value name used as a type
    E0249, // expected constant expr for array length
    E0250, // expected constant expr for array length
    E0318, // can't create default impls for traits outside their crates
    E0319, // trait impls for defaulted traits allowed just for structs/enums
    E0320, // recursive overflow during dropck
    E0321, // extended coherence rules for defaulted traits violated
    E0322, // cannot implement Sized explicitly
    E0323, // implemented an associated const when another trait item expected
    E0324, // implemented a method when another trait item expected
    E0325, // implemented an associated type when another trait item expected
    E0326, // associated const implemented with different type from trait
    E0327, // referred to method instead of constant in match pattern
    E0366, // dropck forbid specialization to concrete type or region
    E0367, // dropck forbid specialization to predicate not in struct/enum
    E0368, // binary operation `<op>=` cannot be applied to types
    E0369, // binary operation `<op>` cannot be applied to types
    E0371, // impl Trait for Trait is illegal
    E0372  // impl Trait for Trait where Trait is not object safe
}
<commit_msg>Add long diagnostic for E0131, E0132<commit_after>// Copyright 2014 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.

#![allow(non_snake_case)]

register_long_diagnostics! {

E0046: r##"
When trying to make some type implement a trait `Foo`, you must, at minimum,
provide implementations for all of `Foo`'s required methods (meaning the
methods that do not have default implementations), as well as any required
trait items like associated types or constants.
"##,

E0054: r##"
It is not allowed to cast to a bool. If you are trying to cast a numeric type
to a bool, you can compare it with zero instead:

```
let x = 5;

// Ok
let x_is_nonzero = x != 0;

// Not allowed, won't compile
let x_is_nonzero = x as bool;
```
"##,

E0062: r##"
This error indicates that during an attempt to build a struct or struct-like
enum variant, one of the fields was specified more than once. Each field should
be specified exactly one time.
"##,

E0063: r##"
This error indicates that during an attempt to build a struct or struct-like
enum variant, one of the fields was not provided. Each field should be specified
exactly once.
"##,

E0067: r##"
The left-hand side of an assignment operator must be an lvalue expression. An
lvalue expression represents a memory location and includes item paths (ie,
namespaced variables), dereferences, indexing expressions, and field references.

```
use std::collections::LinkedList;

// Good
let mut list = LinkedList::new();

// Bad: assignment to non-lvalue expression
LinkedList::new() += 1;
```
"##,

E0081: r##"
Enum discriminants are used to differentiate enum variants stored in memory.
This error indicates that the same value was used for two or more variants,
making them impossible to tell apart.

```
// Good.
enum Enum {
    P,
    X = 3,
    Y = 5
}

// Bad.
enum Enum {
    P = 3,
    X = 3,
    Y = 5
}
```

Note that variants without a manually specified discriminant are numbered from
top to bottom starting from 0, so clashes can occur with seemingly unrelated
variants.

```
enum Bad {
    X,
    Y = 0
}
```

Here `X` will have already been assigned the discriminant 0 by the time `Y` is
encountered, so a conflict occurs.
"##,

E0082: r##"
The default type for enum discriminants is `isize`, but it can be adjusted by
adding the `repr` attribute to the enum declaration. This error indicates that
an integer literal given as a discriminant is not a member of the discriminant
type. For example:

```
#[repr(u8)]
enum Thing {
    A = 1024,
    B = 5
}
```

Here, 1024 lies outside the valid range for `u8`, so the discriminant for `A` is
invalid. You may want to change representation types to fix this, or else change
invalid discriminant values so that they fit within the existing type.

Note also that without a representation manually defined, the compiler will
optimize by using the smallest integer type possible.
"##,

E0083: r##"
At present, it's not possible to define a custom representation for an enum with
a single variant. As a workaround you can add a `Dummy` variant.

See: https://github.com/rust-lang/rust/issues/10292
"##,

E0084: r##"
It is impossible to define an integer type to be used to represent zero-variant
enum values because there are no zero-variant enum values. There is no way to
construct an instance of the following type using only safe code:

```
enum Empty {}
```
"##,

E0131: r##"
It is not possible to define `main` with type parameters, or even with function
parameters. When `main` is present, it must take no arguments and return `()`.
"##,

E0132: r##"
It is not possible to declare type parameters on a function that has the `start`
attribute. Such a function must have the following type signature:

```
fn(isize, *const *const u8) -> isize
```
"##

}

register_diagnostics! {
    E0023,
    E0024,
    E0025,
    E0026,
    E0027,
    E0029,
    E0030,
    E0031,
    E0033,
    E0034,
    E0035,
    E0036,
    E0038,
    E0040, // explicit use of destructor method
    E0044,
    E0045,
    E0049,
    E0050,
    E0053,
    E0055,
    E0057,
    E0059,
    E0060,
    E0061,
    E0066,
    E0068,
    E0069,
    E0070,
    E0071,
    E0072,
    E0073,
    E0074,
    E0075,
    E0076,
    E0077,
    E0085,
    E0086,
    E0087,
    E0088,
    E0089,
    E0090,
    E0091,
    E0092,
    E0093,
    E0094,
    E0101,
    E0102,
    E0103,
    E0104,
    E0106,
    E0107,
    E0116,
    E0117,
    E0118,
    E0119,
    E0120,
    E0121,
    E0122,
    E0123,
    E0124,
    E0127,
    E0128,
    E0129,
    E0130,
    E0141,
    E0159,
    E0163,
    E0164,
    E0166,
    E0167,
    E0168,
    E0172,
    E0173, // manual implementations of unboxed closure traits are experimental
    E0174, // explicit use of unboxed closure methods are experimental
    E0178,
    E0182,
    E0183,
    E0184,
    E0185,
    E0186,
    E0187, // can't infer the kind of the closure
    E0188, // types differ in mutability
    E0189, // can only cast a boxed pointer to a boxed object
    E0190, // can only cast a &-pointer to an &-object
    E0191, // value of the associated type must be specified
    E0192, // negative imples are allowed just for `Send` and `Sync`
    E0193, // cannot bound type where clause bounds may only be attached to types
           // involving type parameters
    E0194,
    E0195, // lifetime parameters or bounds on method do not match the trait declaration
    E0196, // cannot determine a type for this closure
    E0197, // inherent impls cannot be declared as unsafe
    E0198, // negative implementations are not unsafe
    E0199, // implementing trait is not unsafe
    E0200, // trait requires an `unsafe impl` declaration
    E0201, // duplicate method in trait impl
    E0202, // associated items are not allowed in inherent impls
    E0203, // type parameter has more than one relaxed default bound,
           // and only one is supported
    E0204, // trait `Copy` may not be implemented for this type; field
           // does not implement `Copy`
    E0205, // trait `Copy` may not be implemented for this type; variant
           // does not implement `copy`
    E0206, // trait `Copy` may not be implemented for this type; type is
           // not a structure or enumeration
    E0207, // type parameter is not constrained by the impl trait, self type, or predicate
    E0208,
    E0209, // builtin traits can only be implemented on structs or enums
    E0210, // type parameter is not constrained by any local type
    E0211,
    E0212, // cannot extract an associated type from a higher-ranked trait bound
    E0213, // associated types are not accepted in this context
    E0214, // parenthesized parameters may only be used with a trait
    E0215, // angle-bracket notation is not stable with `Fn`
    E0216, // parenthetical notation is only stable with `Fn`
    E0217, // ambiguous associated type, defined in multiple supertraits
    E0218, // no associated type defined
    E0219, // associated type defined in higher-ranked supertrait
    E0220, // associated type not found for type parameter
    E0221, // ambiguous associated type in bounds
    E0222, // variadic function must have C calling convention
    E0223, // ambiguous associated type
    E0224, // at least one non-builtin train is required for an object type
    E0225, // only the builtin traits can be used as closure or object bounds
    E0226, // only a single explicit lifetime bound is permitted
    E0227, // ambiguous lifetime bound, explicit lifetime bound required
    E0228, // explicit lifetime bound required
    E0229, // associated type bindings are not allowed here
    E0230, // there is no type parameter on trait
    E0231, // only named substitution parameters are allowed
    E0232, // this attribute must have a value
    E0233,
    E0234, // `for` loop expression has type which does not implement the `Iterator` trait
    E0235, // structure constructor specifies a structure of type but
    E0236, // no lang item for range syntax
    E0237, // no lang item for range syntax
    E0238, // parenthesized parameters may only be used with a trait
    E0239, // `next` method of `Iterator` trait has unexpected type
    E0240,
    E0241,
    E0242, // internal error looking up a definition
    E0243, // wrong number of type arguments
    E0244, // wrong number of type arguments
    E0245, // not a trait
    E0246, // illegal recursive type
    E0247, // found module name used as a type
    E0248, // found value name used as a type
    E0249, // expected constant expr for array length
    E0250, // expected constant expr for array length
    E0318, // can't create default impls for traits outside their crates
    E0319, // trait impls for defaulted traits allowed just for structs/enums
    E0320, // recursive overflow during dropck
    E0321, // extended coherence rules for defaulted traits violated
    E0322, // cannot implement Sized explicitly
    E0323, // implemented an associated const when another trait item expected
    E0324, // implemented a method when another trait item expected
    E0325, // implemented an associated type when another trait item expected
    E0326, // associated const implemented with different type from trait
    E0327, // referred to method instead of constant in match pattern
    E0366, // dropck forbid specialization to concrete type or region
    E0367, // dropck forbid specialization to predicate not in struct/enum
    E0368, // binary operation `<op>=` cannot be applied to types
    E0369, // binary operation `<op>` cannot be applied to types
    E0371, // impl Trait for Trait is illegal
    E0372  // impl Trait for Trait where Trait is not object safe
}
<|endoftext|>


<commit_before>use atlas_cache::SubImageSpec;
use cache;
use calx_color::Rgba;
use calx_color::color::*;
use euclid::{Rect, rect, Vector2D, vec2};
use std::fmt;
use std::rc::Rc;
use vitral;

pub type Color = [f32; 4];

/// Monochrome layer in a single frame.
#[derive(Clone, PartialEq)]
pub struct Splat {
    pub image: vitral::ImageData<usize>,
    pub offset: Vector2D<f32>,
    pub color: Color,
    pub back_color: Color,
}

impl fmt::Debug for Splat {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Splat {{ {:?}+{:?} {:?} {:?} }}",
            self.image.tex_coords,
            self.offset,
            self.color,
            self.back_color
        )
    }
}

impl Splat {
    pub fn new(geom: &Geom, sheet: String, color: [f32; 4], back_color: [f32; 4]) -> Splat {
        Splat {
            image: cache::get(&SubImageSpec {
                sheet_name: sheet,
                bounds: geom.bounds,
            }),
            offset: geom.offset,
            color: color,
            back_color: back_color,
        }
    }
}

/// Stack of monochrome splat layers making up a drawable colored image.
pub type Frame = Vec<Splat>;

/// Collection of drawable frames.
pub type Brush = Vec<Frame>;

pub struct Builder {
    color: Rgba,
    back_color: Rgba,
    sheet_name: String,
    // The inner splat vectors are the *columns* of the splat matrix, and the merge operation will
    // build frames from the *rows* of the matrix.
    //
    // This sort of structure is used so that it'll be easy to specify a brush that has both a
    // complex structure (wallform or blobform) and has multiple splats for each frame.
    splat_matrix: Vec<Vec<Splat>>,
    pub brush: Brush,
}

/// Builder structure for brushes.
///
/// Building the brush involves some complex standard sequences like blob and wall forms. It also
/// involves combining splats into frames. A complex form with frames consisting of multiple splats
/// is made using the column-adding functions for each splat and the calling `merge` or `finish` to
/// convert them into frames.
impl Builder {
    /// Start a new brush builder using the given sprite sheet from cache.
    ///
    /// You're expected to set up your sprite sheets so that a single brush is built from a single
    /// sheet.
    ///
    /// The default foreground color is white and the default background color is black.
    pub fn new(sheet_name: &str) -> Builder {
        Builder {
            color: WHITE,
            back_color: BLACK,
            sheet_name: sheet_name.to_string(),
            splat_matrix: Vec::new(),
            brush: Vec::new(),
        }
    }

    /// Set the foreground color for the brush.
    pub fn color<C: Into<Rgba>>(mut self, color: C) -> Builder {
        self.color = color.into();
        self
    }

    pub fn sheet(mut self, sheet_name: &str) -> Builder {
        self.sheet_name = sheet_name.to_string();
        self
    }

    /// Set the foreground and background colors for the brush.
    pub fn colors<C: Into<Rgba>, D: Into<Rgba>>(mut self, color: C, back_color: D) -> Builder {
        self.color = color.into();
        self.back_color = back_color.into();
        self
    }

    fn make_splat(&self, geom: &Geom) -> Splat {
        Splat::new(
            geom,
            self.sheet_name.clone(),
            self.color.into_array(),
            self.back_color.into_array(),
        )
    }

    /// Add a splat columnn to the current splat matrix.
    ///
    /// If a splat matrix exists, the column must match the height of the matrix. Otherwise the
    /// column will start a new splat matrix.
    pub fn splat<I: IntoIterator<Item = Geom>>(mut self, geom: I) -> Builder {
        let matrix_column = geom.into_iter()
            .map(|g| self.make_splat(&g))
            .collect::<Vec<Splat>>();
        assert!(
            self.splat_matrix.is_empty() || matrix_column.len() == self.splat_matrix[0].len(),
            "Splat frame count does not match previous parallel splats"
        );
        self.splat_matrix.push(matrix_column);
        self
    }

    /// Add a single-frame splat for a standard tile to the splat matrix.
    pub fn tile(self, x: u32, y: u32) -> Builder { self.splat(Geom::tile(x, y)) }

    /// Add a simple mob that has a bobbing animation
    pub fn mob(self, x: u32, y: u32) -> Builder {
        self.splat(Some(Geom::new(16, 16, x, y, 32, 32)))
            .merge()
            .splat(Some(Geom::new(16, 18, x, y, 32, 32)))
    }

    /// Add a blobform splat column to the splat matrix.
    ///
    /// Blobs are built from three 96x32 strips. First one contains the vertical edges, the second
    /// contains the rear blob and the third contains the blob front. The vertical and rear
    /// frames are nondescript and will probably be reused extensively.
    ///
    /// Blob shaping is somewhat complicated and generates a large number of frames.
    pub fn blob(
        self,
        vert_x: u32,
        vert_y: u32,
        rear_x: u32,
        rear_y: u32,
        x: u32,
        y: u32,
    ) -> Builder {
        self.splat(Geom::blob(vert_x, vert_y, rear_x, rear_y, x, y))
    }

    /// Add a wallform splat column to the splat matrix.
    ///
    /// Wall tiles are chopped up from two 32x32 images. One contains the center pillar wallform
    /// and the other contains the two long sides wallform.
    pub fn wall(self, center_x: u32, center_y: u32, sides_x: u32, sides_y: u32) -> Builder {
        self.splat(Geom::wall(center_x, center_y, sides_x, sides_y))
    }

    /// Merge the rows in the current splat matrix to frames, clear the splat matrix.
    pub fn merge(mut self) -> Builder {
        assert!(
            !self.splat_matrix.is_empty(),
            "Merging without any splats specified"
        );
        let n = self.splat_matrix[0].len();

        for _ in 0..n {
            let mut frame = Vec::new();
            for i in &mut self.splat_matrix {
                frame.push(i.remove(0));
            }
            self.brush.push(frame);
        }

        self.splat_matrix.clear();
        self
    }

    /// Convert the builder into a finished brush.
    ///
    /// If the current splat matrix is not empty, it will be merged into frames first.
    pub fn finish(mut self) -> Rc<Brush> {
        if !self.splat_matrix.is_empty() {
            // A merge is pending, do it now.
            self = self.merge();
        }
        Rc::new(self.brush)
    }
}

pub struct Geom {
    offset: Vector2D<f32>,
    bounds: Rect<u32>,
}

impl Geom {
    pub fn new(
        offset_x: i32,
        offset_y: i32,
        orig_x: u32,
        orig_y: u32,
        width: u32,
        height: u32,
    ) -> Geom {
        Geom {
            offset: vec2(offset_x as f32, offset_y as f32),
            bounds: rect(orig_x, orig_y, width, height),
        }
    }

    pub fn tile(x: u32, y: u32) -> Option<Geom> {
        // The BrushBuilder API expects all geom stuff to be IntoIter, so this returns Option for
        // one-shot iterable instead of a naked value.
        Some(Geom::new(16, 16, x, y, 32, 32))
    }

    pub fn blob(vert_x: u32, vert_y: u32, rear_x: u32, rear_y: u32, x: u32, y: u32) -> Vec<Geom> {
        vec![
            Geom::new(16, 16, vert_x, vert_y, 16, 32),       // 0: Top left    VERTICAL SIDES
            Geom::new(0, 16, vert_x + 16, vert_y, 16, 32),   // 1: Top right
            Geom::new(16, 16, vert_x + 32, vert_y, 16, 32),  // 2: Middle left
            Geom::new(0, 16, vert_x + 48, vert_y, 16, 32),   // 3: Middle right
            Geom::new(16, 16, vert_x + 64, vert_y, 16, 32),  // 4: Bottom left
            Geom::new(0, 16, vert_x + 80, vert_y, 16, 32),   // 5: Bottom right

            Geom::new(16, 16, rear_x, rear_y, 10, 32),       // 6: Left half       REAR PARTS

            Geom::new(6, 16, rear_x + 10, rear_y, 6, 32),    // 7: Front
            Geom::new(0, 16, rear_x + 16, rear_y, 6, 32),    // 8

            Geom::new(-6, 16, rear_x + 22, rear_y, 10, 32),  // 9: Right half

            Geom::new(16, 16, rear_x + 32, rear_y, 10, 32),  // 10: Y-axis slope
            Geom::new(6, 16, rear_x + 42, rear_y, 6, 32),    // 11
            Geom::new(0, 16, rear_x + 48, rear_y, 6, 32),    // 12
            Geom::new(-6, 16, rear_x + 54, rear_y, 10, 32),  // 13

            Geom::new(16, 16, rear_x + 64, rear_y, 10, 32),  // 14: X-axis slope
            Geom::new(6, 16, rear_x + 74, rear_y, 6, 32),    // 15
            Geom::new(0, 16, rear_x + 80, rear_y, 6, 32),    // 16
            Geom::new(-6, 16, rear_x + 86, rear_y, 10, 32),  // 17

            Geom::new(16, 16, x, y, 10, 32),                 // 18 Left half      FRONT PARTS

            Geom::new(6, 16, x + 10, y, 6, 32),              // 19: Front
            Geom::new(0, 16, x + 16, y, 6, 32),              // 20

            Geom::new(-6, 16, x + 22, y, 10, 32),            // 21: Right half

            Geom::new(16, 16, x + 32, y, 10, 32),            // 22: Y-axis slope
            Geom::new(6, 16, x + 42, y, 6, 32),              // 23
            Geom::new(0, 16, x + 48, y, 6, 32),              // 24
            Geom::new(-6, 16, x + 54, y, 10, 32),            // 25

            Geom::new(16, 16, x + 64, y, 10, 32),            // 26: X-axis slope
            Geom::new(6, 16, x + 74, y, 6, 32),              // 27
            Geom::new(0, 16, x + 80, y, 6, 32),              // 28
            Geom::new(-6, 16, x + 86, y, 10, 32),            // 29
        ]
    }

    pub fn wall(center_x: u32, center_y: u32, sides_x: u32, sides_y: u32) -> Vec<Geom> {
        vec![
            Geom::new(16, 16, center_x, center_y, 16, 32),       // 0
            Geom::new(0, 16, center_x + 16, center_y, 16, 32),   // 1
            Geom::new(16, 16, sides_x, sides_y, 16, 32),         // 2
            Geom::new(0, 16, sides_x + 16, sides_y, 16, 32),     // 3
        ]
    }
}
<commit_msg>Some extra bits in brush API<commit_after>use atlas_cache::SubImageSpec;
use cache;
use calx_color::Rgba;
use calx_color::color::*;
use euclid::{Rect, rect, Vector2D, vec2};
use std::fmt;
use std::rc::Rc;
use std::iter::{self, Take, Repeat};
use vitral;

pub type Color = [f32; 4];

/// Monochrome layer in a single frame.
#[derive(Clone, PartialEq)]
pub struct Splat {
    pub image: vitral::ImageData<usize>,
    pub offset: Vector2D<f32>,
    pub color: Color,
    pub back_color: Color,
}

impl fmt::Debug for Splat {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Splat {{ {:?}+{:?} {:?} {:?} }}",
            self.image.tex_coords,
            self.offset,
            self.color,
            self.back_color
        )
    }
}

impl Splat {
    pub fn new(geom: &Geom, sheet: String, color: [f32; 4], back_color: [f32; 4]) -> Splat {
        Splat {
            image: cache::get(&SubImageSpec {
                sheet_name: sheet,
                bounds: geom.bounds,
            }),
            offset: geom.offset,
            color: color,
            back_color: back_color,
        }
    }
}

/// Stack of monochrome splat layers making up a drawable colored image.
pub type Frame = Vec<Splat>;

/// Collection of drawable frames.
pub type Brush = Vec<Frame>;

pub struct Builder {
    color: Rgba,
    back_color: Rgba,
    sheet_name: String,
    // The inner splat vectors are the *columns* of the splat matrix, and the merge operation will
    // build frames from the *rows* of the matrix.
    //
    // This sort of structure is used so that it'll be easy to specify a brush that has both a
    // complex structure (wallform or blobform) and has multiple splats for each frame.
    splat_matrix: Vec<Vec<Splat>>,
    pub brush: Brush,
}

/// Builder structure for brushes.
///
/// Building the brush involves some complex standard sequences like blob and wall forms. It also
/// involves combining splats into frames. A complex form with frames consisting of multiple splats
/// is made using the column-adding functions for each splat and the calling `merge` or `finish` to
/// convert them into frames.
impl Builder {
    /// Start a new brush builder using the given sprite sheet from cache.
    ///
    /// You're expected to set up your sprite sheets so that a single brush is built from a single
    /// sheet.
    ///
    /// The default foreground color is white and the default background color is black.
    pub fn new(sheet_name: &str) -> Builder {
        Builder {
            color: WHITE,
            back_color: BLACK,
            sheet_name: sheet_name.to_string(),
            splat_matrix: Vec::new(),
            brush: Vec::new(),
        }
    }

    /// Set the foreground color for the brush.
    pub fn color<C: Into<Rgba>>(mut self, color: C) -> Builder {
        self.color = color.into();
        self
    }

    pub fn sheet(mut self, sheet_name: &str) -> Builder {
        self.sheet_name = sheet_name.to_string();
        self
    }

    /// Set the foreground and background colors for the brush.
    pub fn colors<C: Into<Rgba>, D: Into<Rgba>>(mut self, color: C, back_color: D) -> Builder {
        self.color = color.into();
        self.back_color = back_color.into();
        self
    }

    fn make_splat(&self, geom: &Geom) -> Splat {
        Splat::new(
            geom,
            self.sheet_name.clone(),
            self.color.into_array(),
            self.back_color.into_array(),
        )
    }

    /// Add a splat columnn to the current splat matrix.
    ///
    /// If a splat matrix exists, the column must match the height of the matrix. Otherwise the
    /// column will start a new splat matrix.
    pub fn splat<I: IntoIterator<Item = Geom>>(mut self, geom: I) -> Builder {
        let matrix_column = geom.into_iter()
            .map(|g| self.make_splat(&g))
            .collect::<Vec<Splat>>();
        assert!(
            self.splat_matrix.is_empty() || matrix_column.len() == self.splat_matrix[0].len(),
            "Splat frame count {} does not match previous parallel splats with count {}",
            matrix_column.len(), self.splat_matrix[0].len()
        );
        self.splat_matrix.push(matrix_column);
        self
    }

    /// Add a single-frame splat for a standard tile to the splat matrix.
    pub fn tile(self, x: u32, y: u32) -> Builder { self.splat(Geom::tile(x, y)) }

    /// Add a multiple frame splat for a standard tile to the splat matrix.
    pub fn tiles(self, n: usize, x: u32, y: u32) -> Builder { self.splat(Geom::tiles(n, x, y)) }

    /// Add a simple mob that has a bobbing animation
    pub fn mob(self, x: u32, y: u32) -> Builder {
        self.splat(Some(Geom::new(16, 16, x, y, 32, 32)))
            .merge()
            .splat(Some(Geom::new(16, 18, x, y, 32, 32)))
    }

    /// Add a blobform splat column to the splat matrix.
    ///
    /// Blobs are built from three 96x32 strips. First one contains the vertical edges, the second
    /// contains the rear blob and the third contains the blob front. The vertical and rear
    /// frames are nondescript and will probably be reused extensively.
    ///
    /// Blob shaping is somewhat complicated and generates a large number of frames.
    pub fn blob(
        self,
        vert_x: u32,
        vert_y: u32,
        rear_x: u32,
        rear_y: u32,
        x: u32,
        y: u32,
    ) -> Builder {
        self.splat(Geom::blob(vert_x, vert_y, rear_x, rear_y, x, y))
    }

    /// Add a wallform splat column to the splat matrix.
    ///
    /// Wall tiles are chopped up from two 32x32 images. One contains the center pillar wallform
    /// and the other contains the two long sides wallform.
    pub fn wall(self, center_x: u32, center_y: u32, sides_x: u32, sides_y: u32) -> Builder {
        self.splat(Geom::wall(center_x, center_y, sides_x, sides_y))
    }

    /// Merge the rows in the current splat matrix to frames, clear the splat matrix.
    pub fn merge(mut self) -> Builder {
        assert!(
            !self.splat_matrix.is_empty(),
            "Merging without any splats specified"
        );
        let n = self.splat_matrix[0].len();

        for _ in 0..n {
            let mut frame = Vec::new();
            for i in &mut self.splat_matrix {
                frame.push(i.remove(0));
            }
            self.brush.push(frame);
        }

        self.splat_matrix.clear();
        self
    }

    /// Convert the builder into a finished brush.
    ///
    /// If the current splat matrix is not empty, it will be merged into frames first.
    pub fn finish(mut self) -> Rc<Brush> {
        if !self.splat_matrix.is_empty() {
            // A merge is pending, do it now.
            self = self.merge();
        }
        Rc::new(self.brush)
    }
}

#[derive(Clone)]
pub struct Geom {
    offset: Vector2D<f32>,
    bounds: Rect<u32>,
}

impl Geom {
    pub fn new(
        offset_x: i32,
        offset_y: i32,
        orig_x: u32,
        orig_y: u32,
        width: u32,
        height: u32,
    ) -> Geom {
        Geom {
            offset: vec2(offset_x as f32, offset_y as f32),
            bounds: rect(orig_x, orig_y, width, height),
        }
    }

    pub fn tile(x: u32, y: u32) -> Option<Geom> {
        // The BrushBuilder API expects all geom stuff to be IntoIter, so this returns Option for
        // one-shot iterable instead of a naked value.
        Some(Geom::new(16, 16, x, y, 32, 32))
    }

    pub fn tiles(n: usize, x: u32, y: u32) -> Take<Repeat<Geom>> {
        // The BrushBuilder API expects all geom stuff to be IntoIter, so this returns Option for
        // one-shot iterable instead of a naked value.
        iter::repeat(Geom::new(16, 16, x, y, 32, 32)).take(n)
    }

    pub fn blob(vert_x: u32, vert_y: u32, rear_x: u32, rear_y: u32, x: u32, y: u32) -> Vec<Geom> {
        vec![
            Geom::new(16, 16, vert_x, vert_y, 16, 32),       // 0: Top left    VERTICAL SIDES
            Geom::new(0, 16, vert_x + 16, vert_y, 16, 32),   // 1: Top right
            Geom::new(16, 16, vert_x + 32, vert_y, 16, 32),  // 2: Middle left
            Geom::new(0, 16, vert_x + 48, vert_y, 16, 32),   // 3: Middle right
            Geom::new(16, 16, vert_x + 64, vert_y, 16, 32),  // 4: Bottom left
            Geom::new(0, 16, vert_x + 80, vert_y, 16, 32),   // 5: Bottom right

            Geom::new(16, 16, rear_x, rear_y, 10, 32),       // 6: Left half       REAR PARTS

            Geom::new(6, 16, rear_x + 10, rear_y, 6, 32),    // 7: Front
            Geom::new(0, 16, rear_x + 16, rear_y, 6, 32),    // 8

            Geom::new(-6, 16, rear_x + 22, rear_y, 10, 32),  // 9: Right half

            Geom::new(16, 16, rear_x + 32, rear_y, 10, 32),  // 10: Y-axis slope
            Geom::new(6, 16, rear_x + 42, rear_y, 6, 32),    // 11
            Geom::new(0, 16, rear_x + 48, rear_y, 6, 32),    // 12
            Geom::new(-6, 16, rear_x + 54, rear_y, 10, 32),  // 13

            Geom::new(16, 16, rear_x + 64, rear_y, 10, 32),  // 14: X-axis slope
            Geom::new(6, 16, rear_x + 74, rear_y, 6, 32),    // 15
            Geom::new(0, 16, rear_x + 80, rear_y, 6, 32),    // 16
            Geom::new(-6, 16, rear_x + 86, rear_y, 10, 32),  // 17

            Geom::new(16, 16, x, y, 10, 32),                 // 18 Left half      FRONT PARTS

            Geom::new(6, 16, x + 10, y, 6, 32),              // 19: Front
            Geom::new(0, 16, x + 16, y, 6, 32),              // 20

            Geom::new(-6, 16, x + 22, y, 10, 32),            // 21: Right half

            Geom::new(16, 16, x + 32, y, 10, 32),            // 22: Y-axis slope
            Geom::new(6, 16, x + 42, y, 6, 32),              // 23
            Geom::new(0, 16, x + 48, y, 6, 32),              // 24
            Geom::new(-6, 16, x + 54, y, 10, 32),            // 25

            Geom::new(16, 16, x + 64, y, 10, 32),            // 26: X-axis slope
            Geom::new(6, 16, x + 74, y, 6, 32),              // 27
            Geom::new(0, 16, x + 80, y, 6, 32),              // 28
            Geom::new(-6, 16, x + 86, y, 10, 32),            // 29
        ]
    }

    pub fn wall(center_x: u32, center_y: u32, sides_x: u32, sides_y: u32) -> Vec<Geom> {
        vec![
            Geom::new(16, 16, center_x, center_y, 16, 32),       // 0
            Geom::new(0, 16, center_x + 16, center_y, 16, 32),   // 1
            Geom::new(16, 16, sides_x, sides_y, 16, 32),         // 2
            Geom::new(0, 16, sides_x + 16, sides_y, 16, 32),     // 3
        ]
    }
}
<|endoftext|>


<commit_before>// extern crate bindgen;
#[cfg(target_env = "msvc")]
extern crate cc;
extern crate pkg_config;
extern crate num_cpus;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

// Automatically write bindings to libsass
//#[allow(dead_code)]
//fn write_bindings() {
//    let bindings = bindgen::Builder::default()
//        .header("libsass/include/sass.h")
//        .clang_arg("-Ilibsass/include")
//        // To avoid a test failing
//        .blacklist_type("max_align_t")
//        // we do static linking so it should be fine
//        // https://github.com/rust-lang/rust/issues/36927
//        .rustified_enum(".*")
//        .generate()
//        .expect("Unable to generate bindings");
//
//    // Write the bindings to the $OUT_DIR/bindings.rs file.
//    let out_path = PathBuf::from("src");
//    bindings
//        .write_to_file(out_path.join("bindings.rs"))
//        .expect("Couldn't write bindings!");
//}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => panic!("\n{} failed with {}\n", stringify!($e), e),
    })
}

fn cp_r(dir: &Path, dest: &Path) {
    for entry in t!(fs::read_dir(dir)) {
        let entry = t!(entry);
        let path = entry.path();
        let dst = dest.join(path.file_name().expect("Failed to get filename of path"));
        if t!(fs::metadata(&path)).is_file() {
            t!(fs::copy(path, dst));
        } else {
            t!(fs::create_dir_all(&dst));
            cp_r(&path, &dst);
        }
    }
}

fn get_libsass_folder() -> PathBuf {
    env::current_dir().expect("Failed to get the current directory").join("libsass")
}

#[cfg(target_os = "linux")]
fn _compile(libprobe: fn(&str) -> bool) {
    if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else {
        panic!("no c++ library found");
    }
}

#[cfg(not(target_os = "linux"))]
fn _compile(libprobe: fn(&str) -> bool) {
    if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else {
        panic!("no c++ library found");
    }
}

// linux/unix
#[cfg(not(target_env = "msvc"))]
fn compile() {
    let target = env::var("TARGET").expect("TARGET not found");
    let src = get_libsass_folder();
    let dest = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR not found"));
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);
    let is_bsd = target.contains("dragonfly")
        || target.contains("freebsd")
        || target.contains("netbsd")
        || target.contains("openbsd");
    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or_else(|_| num_cpus::get().to_string());
    let r = Command::new(if is_bsd { "gmake" } else { "make" })
        .current_dir(&build)
        .args(&["--jobs", &jobs])
        .output()
        .expect("Failed to run make/gmake. Do you have it installed?");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=sass");

    let libprobe = | lib: &str | -> bool {
      Command::new("cc")
        .arg("-xc++")
        .arg("-o/dev/null")
        .arg(format!("-l{}",lib))
        .arg("-shared")
        .stderr(Stdio::null())
        .status()
        .expect("Failed to run cc. Do you have it installed?")
        .success()
    };
    _compile(libprobe);
}

// windows
#[cfg(target_env = "msvc")]
fn compile() {
    let src = get_libsass_folder();
    let target = env::var("TARGET").expect("TARGET not found in environment");
    let msvc_platform = if target.contains("x86_64") {
        "Win64"
    } else {
        "Win32"
    };
    let dest = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR not found in environment"));
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);

    // Find an instance of devenv.exe from Visual Studio IDE in order to upgrade
    // libsass.sln to the current available IDE. Do nothing if no devenv.exe found
    let d = cc::windows_registry::find(target.as_str(), "devenv.exe");
    if let Some(mut d) = d {
        let d = d
            .args(&["/upgrade", "win\\libsass.sln"])
            .current_dir(&build)
            .output()
            .expect("Failed to run devenv. Do you have it installed?");
        if !d.status.success() {
            let err = String::from_utf8_lossy(&d.stderr);
            let out = String::from_utf8_lossy(&d.stdout);
            println!("Upgrade error:\nSTDERR:{}\nSTDOUT:{}", err, out);
        }
    }

    let search = Command::new("where")
        .args(&["msbuild.exe"])
        .output()
        .expect("Failed to run where: Could not search for msbuild.exe on path.");
    let mut msbuild = if search.status.success() {
        Command::new("msbuild.exe")
    } else {
        cc::windows_registry::find(target.as_str(), "msbuild.exe")
            .expect("Could not find msbuild.exe on the registry")
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or_else(|_| num_cpus::get().to_string());

    let r = msbuild
        .args(&[
            "win\\libsass.sln",
            "/p:LIBSASS_STATIC_LIB=1",
            "/p:Configuration=Release",
            "/p:WholeProgramOptimization=false",
            format!("/m:{}", jobs).as_str(),
            format!("/p:Platform={}", msvc_platform).as_str(),
        ])
        .current_dir(&build)
        .output()
        .expect("Failed to run msbuild. Do you have it installed?");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("win").join("bin").display()
    );
    println!("cargo:rustc-link-lib=static=libsass");
}

fn main() {
    // Uncomment the line below to generate bindings. Doesn't work on CI as it
    // requires additional tooling
    // write_bindings();

    // Is it already built?
    if let Ok(_) = pkg_config::find_library("libsass") {
        return;
    }

    compile();
}
<commit_msg>Conditionally call submodule update<commit_after>// extern crate bindgen;
#[cfg(target_env = "msvc")]
extern crate cc;
extern crate pkg_config;
extern crate num_cpus;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

// Automatically write bindings to libsass
//#[allow(dead_code)]
//fn write_bindings() {
//    let bindings = bindgen::Builder::default()
//        .header("libsass/include/sass.h")
//        .clang_arg("-Ilibsass/include")
//        // To avoid a test failing
//        .blacklist_type("max_align_t")
//        // we do static linking so it should be fine
//        // https://github.com/rust-lang/rust/issues/36927
//        .rustified_enum(".*")
//        .generate()
//        .expect("Unable to generate bindings");
//
//    // Write the bindings to the $OUT_DIR/bindings.rs file.
//    let out_path = PathBuf::from("src");
//    bindings
//        .write_to_file(out_path.join("bindings.rs"))
//        .expect("Couldn't write bindings!");
//}

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => panic!("\n{} failed with {}\n", stringify!($e), e),
    })
}

fn cp_r(dir: &Path, dest: &Path) {
    for entry in t!(fs::read_dir(dir)) {
        let entry = t!(entry);
        let path = entry.path();
        let dst = dest.join(path.file_name().expect("Failed to get filename of path"));
        if t!(fs::metadata(&path)).is_file() {
            t!(fs::copy(path, dst));
        } else {
            t!(fs::create_dir_all(&dst));
            cp_r(&path, &dst);
        }
    }
}

fn get_libsass_folder() -> PathBuf {
    env::current_dir().expect("Failed to get the current directory").join("libsass")
}

#[cfg(target_os = "linux")]
fn _compile(libprobe: fn(&str) -> bool) {
    if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else {
        panic!("no c++ library found");
    }
}

#[cfg(not(target_os = "linux"))]
fn _compile(libprobe: fn(&str) -> bool) {
    if libprobe("c++_shared") {
        println!("cargo:rustc-link-lib=dylib=c++_shared");
    } else if libprobe("c++") {
        println!("cargo:rustc-link-lib=dylib=c++");
    } else if libprobe("stdc++") {
        println!("cargo:rustc-link-lib=dylib=stdc++");
    } else {
        panic!("no c++ library found");
    }
}

// linux/unix
#[cfg(not(target_env = "msvc"))]
fn compile() {
    let target = env::var("TARGET").expect("TARGET not found");
    let src = get_libsass_folder();
    let dest = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR not found"));
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);
    let is_bsd = target.contains("dragonfly")
        || target.contains("freebsd")
        || target.contains("netbsd")
        || target.contains("openbsd");
    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or_else(|_| num_cpus::get().to_string());
    let r = Command::new(if is_bsd { "gmake" } else { "make" })
        .current_dir(&build)
        .args(&["--jobs", &jobs])
        .output()
        .expect("Failed to run make/gmake. Do you have it installed?");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("lib").display()
    );
    println!("cargo:rustc-link-lib=static=sass");

    let libprobe = | lib: &str | -> bool {
      Command::new("cc")
        .arg("-xc++")
        .arg("-o/dev/null")
        .arg(format!("-l{}",lib))
        .arg("-shared")
        .stderr(Stdio::null())
        .status()
        .expect("Failed to run cc. Do you have it installed?")
        .success()
    };
    _compile(libprobe);
}

// windows
#[cfg(target_env = "msvc")]
fn compile() {
    let src = get_libsass_folder();
    let target = env::var("TARGET").expect("TARGET not found in environment");
    let msvc_platform = if target.contains("x86_64") {
        "Win64"
    } else {
        "Win32"
    };
    let dest = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR not found in environment"));
    let build = dest.join("build");
    t!(fs::create_dir_all(&build));
    cp_r(&src, &build);

    // Find an instance of devenv.exe from Visual Studio IDE in order to upgrade
    // libsass.sln to the current available IDE. Do nothing if no devenv.exe found
    let d = cc::windows_registry::find(target.as_str(), "devenv.exe");
    if let Some(mut d) = d {
        let d = d
            .args(&["/upgrade", "win\\libsass.sln"])
            .current_dir(&build)
            .output()
            .expect("Failed to run devenv. Do you have it installed?");
        if !d.status.success() {
            let err = String::from_utf8_lossy(&d.stderr);
            let out = String::from_utf8_lossy(&d.stdout);
            println!("Upgrade error:\nSTDERR:{}\nSTDOUT:{}", err, out);
        }
    }

    let search = Command::new("where")
        .args(&["msbuild.exe"])
        .output()
        .expect("Failed to run where: Could not search for msbuild.exe on path.");
    let mut msbuild = if search.status.success() {
        Command::new("msbuild.exe")
    } else {
        cc::windows_registry::find(target.as_str(), "msbuild.exe")
            .expect("Could not find msbuild.exe on the registry")
    };

    let jobs = env::var("MAKE_LIBSASS_JOBS").unwrap_or_else(|_| num_cpus::get().to_string());

    let r = msbuild
        .args(&[
            "win\\libsass.sln",
            "/p:LIBSASS_STATIC_LIB=1",
            "/p:Configuration=Release",
            "/p:WholeProgramOptimization=false",
            format!("/m:{}", jobs).as_str(),
            format!("/p:Platform={}", msvc_platform).as_str(),
        ])
        .current_dir(&build)
        .output()
        .expect("Failed to run msbuild. Do you have it installed?");

    if !r.status.success() {
        let err = String::from_utf8_lossy(&r.stderr);
        let out = String::from_utf8_lossy(&r.stdout);
        panic!("Build error:\nSTDERR:{}\nSTDOUT:{}", err, out);
    }

    println!(
        "cargo:rustc-link-search=native={}",
        build.join("win").join("bin").display()
    );
    println!("cargo:rustc-link-lib=static=libsass");
}

fn main() {
    // Uncomment the line below to generate bindings. Doesn't work on CI as it
    // requires additional tooling
    // write_bindings();

    // Is it already built?
    if let Ok(_) = pkg_config::find_library("libsass") {
        return;
    }

    if !Path::new("libsass/.git").exists() {
        let _ = Command::new("git")
            .args(&["submodule", "update", "--init"])
            .status();
    }

    // If it still doesn't exist, stop there
    if !Path::new("libsass/.git").exists() {
        return;
    }

    compile();
}
<|endoftext|>


<commit_before>// TODO: Add License information.

//! Module to parse ESIL strings and convert them into the IR.
//!
//! ESIL (Evaluable Strings Intermediate Language) is the IL used by radare2.
//!
//! For a complete documentation of ESIL please check
//!  [wiki](https://github.com/radare/radare2/wiki/ESIL).
//!
//! # Details
//!
//! The `Parser` struct provides methods needed to convert a valid ESIL string
//! into the IR. `Parser::parse()` parses the ESIL string and returns an `Err`
//! if the ESIL string is Invalid.
//!
//! `Parser` also provides `Parser::emit_insts()` to extract the `Instructions`
//! it generates. Calling `Parser::parse()` several times will add more instructions.
//!
//! # Example
//!
//! ```
//! use radeco::frontend::esil;
//! let esil = "eax,ebx,^=";
//! let mut p = esil::Parser::new();
//! p.parse(esil, None);
//! for inst in &p.emit_insts() {
//!     println!("{}", inst);
//! }
//! ```

extern crate num;
use self::num::traits::Num;

use std::collections::HashMap;
use std::cmp;
use regex::Regex;

use super::{Instruction, Value, Opcode, Location, Address, Arity};

// Macro to return a new hash given (key, value) tuples.
// Example: hash![("foo", "bar"), ("bar", "baz")]
macro_rules! hash {
    ( $( ($x:expr, $y:expr) ),* ) => {
        {
            let mut temp_hash = HashMap::new();
            $(
                temp_hash.insert($x, $y);
             )*
                temp_hash
        }
    };
}

macro_rules! hex_to_i {
    ( $x:expr ) => {
        Num::from_str_radix($x.trim_left_matches("0x"), 16)
    };
}

#[derive(Debug)]
pub enum ParseError {
    InvalidOperator,
    InsufficientOperands,
}

fn map_esil_to_opset() -> HashMap<&'static str, Opcode> {
    // Make a map from esil string to struct Operator.
    // (operator: &str, op: Operator).
    // Possible Optimization:  Move to compile-time generation ?
    hash![
        ("==" , Opcode::OpCmp),
        ("<"  , Opcode::OpLt),
        (">"  , Opcode::OpGt),
        ("<=" , Opcode::OpGteq),
        (">=" , Opcode::OpLteq),
        ("<<" , Opcode::OpLsl),
        (">>" , Opcode::OpLsr),
        ("&"  , Opcode::OpAnd),
        ("|"  , Opcode::OpOr),
        ("="  , Opcode::OpEq),
        ("*"  , Opcode::OpMul),
        ("^"  , Opcode::OpXor),
        ("+"  , Opcode::OpAdd),
        ("-"  , Opcode::OpSub),
        ("/"  , Opcode::OpDiv),
        ("%"  , Opcode::OpMod),
        ("?{" , Opcode::OpIf),
        ("!"  , Opcode::OpNot),
        ("--" , Opcode::OpDec),
        ("++" , Opcode::OpInc),
        ("}"  , Opcode::OpCl)
    ]
}

fn init_regset() -> HashMap<&'static str, u8> {
    // Use from sdb later, probably a better option.
    hash![
        ("rax", 64),
        ("rbx", 64),
        ("rcx", 64),
        ("rdx", 64),
        ("rsp", 64),
        ("rbp", 64),
        ("rsi", 64),
        ("rdi", 64),
        ("rip", 64),
        ("zf",   1)
    ]
}

pub struct Parser<'a> {
    stack: Vec<Value>,
    insts: Vec<Instruction>,
    opset: HashMap<&'a str, Opcode>,
    regset: HashMap<&'a str, u8>,
    tmp_index: u64,
    default_size: u8,
    // The address the parser is currently parsing at.
    addr: Address,
    // Name of the Instruction pointer for the architecture.
    ip: String,
}

impl<'a> Parser<'a> {
    pub fn new() -> Parser<'a> {
        Parser {
            stack: Vec::new(),
            insts: Vec::new(),
            opset: map_esil_to_opset(),
            regset: init_regset(),
            tmp_index: 0,
            // TODO: change this default based on arch.
            default_size: 64,
            addr: 0,
            // TODO: Set dynamically based on the arch.
            ip: "rip".to_string(),
        }
    }

    fn get_tmp_register(&mut self, mut size: u8) -> Value {
        self.tmp_index += 1;
        if size == 0 {
            size = self.default_size;
        }
        Value::tmp(self.tmp_index, size)
    }

    fn add_widen_inst(&mut self, op: &mut Value, size: u8) {
        if op.size > size {
            return;
        }
        let dst = self.get_tmp_register(size);
        let operator = Opcode::OpWiden;
        self.insts.push(Instruction::new(operator, dst.clone(), op.clone(), Value::constant(size as i64), Some(self.addr)));
        *op = dst;
    }

    fn add_narrow_inst(&mut self, op: &mut Value, size: u8) {
        if op.size < size {
            return;
        }
        let dst = self.get_tmp_register(size);
        let operator = Opcode::OpNarrow;
        self.insts.push(Instruction::new(operator, dst.clone(), op.clone(), Value::constant(size as i64), Some(self.addr)));
        *op = dst;
    }

    fn add_assign_inst(&mut self, op: Opcode) -> Result<(), ParseError> {
        let dst = match self.stack.pop() {
            Some(ele) => ele,
            None => return Err(ParseError::InsufficientOperands),
        };

        let mut op1 = match self.stack.pop() {
            Some(ele) => ele,
            None => return Err(ParseError::InsufficientOperands),
        };

        // If it is an assignment to the Instruction Pointer, then the Instruction should be a OpJmp
        // rather than an OpEq.
        if dst.name == self.ip {
            self.insts.push(Instruction::new(Opcode::OpJmp, Value::null(), op1, Value::null(), Some(self.addr)));
            return Ok(());
        }

        if dst.size == op1.size {
            self.insts.push(Instruction::new(op, dst.clone(), op1, Value::null(), Some(self.addr)));
            return Ok(());
        }

        if dst.size > op1.size {
            self.add_widen_inst(&mut op1, dst.size);
        } else {
            self.add_narrow_inst(&mut op1, dst.size);
        }

        // We don't need to use another instruction for assignment. Just replace the dst of the
        // narrow/widen instruction generated.
        self.insts.last_mut().unwrap().dst = dst.clone();
        Ok(())
    }

    fn add_inst(&mut self, op: Opcode) -> Result<(), ParseError> {
        // Handle "}".
        if op == Opcode::OpCl {
            let null = Value::null();
            self.insts.push(Instruction::new(op, null.clone(), null.clone(), null.clone(), Some(self.addr)));
            return Ok(());
        }

        // Assignment operation has to be handled quite differently.
        if op == Opcode::OpEq {
            return self.add_assign_inst(op);
        }

        let mut op2 = match self.stack.pop() {
            Some(ele) => ele,
            None => return Err(ParseError::InsufficientOperands),
        };

        let mut op1 = Value::null();
        if op.to_operator().arity == Arity::Binary {
            op1 = match self.stack.pop() {
                Some(ele) => ele,
                None => return Err(ParseError::InsufficientOperands),
            };
        }

        if op == Opcode::OpIf {
            self.insts.push(Instruction::new(op, Value::null(), op2, op1, Some(self.addr)));
            return Ok(());
        }

        let mut dst_size: u8;
        let mut dst: Value;
        dst_size = cmp::max(op1.size, op2.size);
        dst = self.get_tmp_register(dst_size);

        // Add a check to see if dst, op1 and op2 have the same size.
        // If they do not, cast it. op2 is never 'Null'.
        assert!(op2.location != Location::Null);

        if op.to_operator().arity == Arity::Binary {
            if op1.size > op2.size {
                dst_size = op1.size;
                self.add_widen_inst(&mut op2, op1.size);
            } else if op2.size > op1.size {
                dst_size = op2.size;
                self.add_widen_inst(&mut op1, op2.size);
            }
        }

        dst.size = dst_size;

        self.insts.push(Instruction::new(op, dst.clone(), op2, op1, Some(self.addr)));
        self.stack.push(dst);

        Ok(())
    }

    pub fn parse(&mut self, esil: String, _addr: Option<u64>) -> Result<(), ParseError> {
        // Set parser address.
        self.addr = match _addr {
            Some(s) => {
                s
            },
            None => self.addr + 1,
        };

        let esil: Vec<String> = esil.split(',')
                                    .map(|x| x.to_string()).collect();
        for token in esil {
            let op = match self.opset.get(&*token) {
                Some(op) => op.clone(),
                None => Opcode::OpInvalid,
            };

            if op != Opcode::OpInvalid {
                try!(self.add_inst(op));
                continue;
            }

            // If it contains atleast one alpha, it cannot be an operator.
            let re = Regex::new("[a-zA-Z]").unwrap();
            if re.is_match(&*token) {
                let mut val_type = Location::Unknown;
                let mut val: i64 = 0;
                let mut size: u8 = self.default_size;
                if let Some(r) = self.regset.get(&*token) {
                    val_type = Location::Register;
                    // For now, reg is just a u8.
                    size = *r;
                } else if let Ok(v) = token.parse::<i64>() {
                    val_type = Location::Constant;
                    val = v;
                } else if let Ok(v) = hex_to_i!(token) {
                    val_type = Location::Constant;
                    val = v;
                }
                let v = Value::new(String::from(token), size, val_type, val, 0);
                self.stack.push(v);
                continue;
            }

            // Handle constants.
            if let Ok(num) = token.parse::<i64>() {
                let val_type = Location::Constant;
                let val = num;
                let size  = self.default_size;
                let name = format!("0x{:x}", num);
                let v = Value::new(name, size, val_type, val, 0);
                self.stack.push(v);
                continue;
            }

            // Deal with normal 'composite' instructions.
            if token.char_indices().last().unwrap().1 != ']' {
                let mut dst: Value;
                if let Some(x) = self.stack.last() {
                    dst = x.clone();
                } else {
                    return Err(ParseError::InsufficientOperands);
                }
                let re = Regex::new(r"^(.|..)=$").unwrap();
                let t = re.captures(&*token).unwrap().at(1).unwrap_or("");
                if t.len() == 0 {
                    return Err(ParseError::InvalidOperator);
                }
                let op = match self.opset.get(t) {
                    Some(op) => op.clone(),
                    None => return Err(ParseError::InvalidOperator),
                };

                try!(self.add_inst(op));
                self.stack.push(dst);
                try!(self.add_inst(Opcode::OpEq));
                continue;
            }

            // Deal with memaccess 'composite' instructions.
            let re = Regex::new(r"^(.|..)?(=)?\[([1248]?)\]$").unwrap();
            let tokens = re.captures(&*token).unwrap();
            let eq = tokens.at(2).unwrap_or("");
            let has_op = tokens.at(1).unwrap_or("");
            let access_size = tokens.at(3).unwrap_or("");
            let access_size = match access_size {
                "" => self.default_size,
                _ => access_size.parse::<u8>().unwrap() * 8,
            };

            try!(self.add_inst(Opcode::OpRef));
            // Set the correct size.
            let mut x = self.stack.pop().unwrap();
            self.add_narrow_inst(&mut x, access_size);
            let tmp_dst1 = x.clone();
            self.stack.push(x);

            // Simple 'peek' ([n])
            if eq.is_empty() {
                continue;
            }

            // Simple 'poke' (=[n])
            if has_op.is_empty() {
                try!(self.add_inst(Opcode::OpEq));
                continue;
            }

            // 'poke' with another operation. (<op>=[n])
            let o = match self.opset.get(has_op) {
                Some(x) => x.clone(),
                // Return with error
                None => return Err(ParseError::InvalidOperator),
            };
            try!(self.add_inst(o));
            // Reassignment.
            self.stack.push(tmp_dst1);
            try!(self.add_inst(Opcode::OpEq));
        }
        Ok(())
    }

    /// Emit-instructions converts certain compound instructions to simpler instructions.
    /// For example, the sequence of instructions,
    /// ```none
    /// if zf            (OpIf)
    ///   jump addr      (OpJmp)
    /// ```
    /// is converted to a single conditional jump as,
    /// ```none
    /// if zf jump addr  (OpCJmp)
    /// ```

    pub fn emit_insts(&mut self) -> Vec<Instruction> {
        let len = self.insts.len();
        let mut res: Vec<Instruction> = Vec::new();
        let mut i = 0;
        while i < len {
            let inst = self.insts[i].clone();
            if inst.opcode != Opcode::OpIf {
                res.push(inst);
                i += 1;
                continue;
            }

            // Note(sushant94): We need to improve this process if we want to retain the side-effects.
            // For example, a x86 call instruction will result the corresponding esil statement to
            // have instructions to set esp and then jump to the required location.
            // We can use these side-effects to determine if it's a 'call' or just a jump.
            // This however requires study into other architectures and their esil too.
            while self.insts[i].opcode != Opcode::OpCl && i < len - 1 {
                i += 1;
                let inst_ = self.insts[i].clone();
                if inst_.opcode == Opcode::OpJmp {
                    let res_inst =
                        Instruction::new(Opcode::OpCJmp, Value::null(),
                                         inst.operand_1.clone(),
                                         inst_.operand_1, Some(inst.addr));
                    res.push(res_inst);
                }
            }
            i += 1;
        }
        self.insts = res;
        return (self).insts.clone();
    }
}

#[test]
fn testing() {
    let mut p = Parser::new();
    p.parse("0,0x204db1,rip,+,[1],==,%z,zf,=,%b8,cf,=,%p,pf,=,%s,sf,=", None);
}
<commit_msg>Fix build<commit_after>// TODO: Add License information.

//! Module to parse ESIL strings and convert them into the IR.
//!
//! ESIL (Evaluable Strings Intermediate Language) is the IL used by radare2.
//!
//! For a complete documentation of ESIL please check
//!  [wiki](https://github.com/radare/radare2/wiki/ESIL).
//!
//! # Details
//!
//! The `Parser` struct provides methods needed to convert a valid ESIL string
//! into the IR. `Parser::parse()` parses the ESIL string and returns an `Err`
//! if the ESIL string is Invalid.
//!
//! `Parser` also provides `Parser::emit_insts()` to extract the `Instructions`
//! it generates. Calling `Parser::parse()` several times will add more instructions.
//!
//! # Example
//!
//! ```
//! use radeco::frontend::esil;
//! let esil = "eax,ebx,^=";
//! let mut p = esil::Parser::new();
//! p.parse(esil, None);
//! for inst in &p.emit_insts() {
//!     println!("{}", inst);
//! }
//! ```

extern crate num;
use self::num::traits::Num;

use std::collections::HashMap;
use std::cmp;
use regex::Regex;

use super::{Instruction, Value, Opcode, Location, Address, Arity};

// Macro to return a new hash given (key, value) tuples.
// Example: hash![("foo", "bar"), ("bar", "baz")]
macro_rules! hash {
    ( $( ($x:expr, $y:expr) ),* ) => {
        {
            let mut temp_hash = HashMap::new();
            $(
                temp_hash.insert($x, $y);
             )*
                temp_hash
        }
    };
}

macro_rules! hex_to_i {
    ( $x:expr ) => {
        Num::from_str_radix($x.trim_left_matches("0x"), 16)
    };
}

#[derive(Debug)]
pub enum ParseError {
    InvalidOperator,
    InsufficientOperands,
}

fn map_esil_to_opset() -> HashMap<&'static str, Opcode> {
    // Make a map from esil string to struct Operator.
    // (operator: &str, op: Operator).
    // Possible Optimization:  Move to compile-time generation ?
    hash![
        ("==" , Opcode::OpCmp),
        ("<"  , Opcode::OpLt),
        (">"  , Opcode::OpGt),
        ("<=" , Opcode::OpGteq),
        (">=" , Opcode::OpLteq),
        ("<<" , Opcode::OpLsl),
        (">>" , Opcode::OpLsr),
        ("&"  , Opcode::OpAnd),
        ("|"  , Opcode::OpOr),
        ("="  , Opcode::OpEq),
        ("*"  , Opcode::OpMul),
        ("^"  , Opcode::OpXor),
        ("+"  , Opcode::OpAdd),
        ("-"  , Opcode::OpSub),
        ("/"  , Opcode::OpDiv),
        ("%"  , Opcode::OpMod),
        ("?{" , Opcode::OpIf),
        ("!"  , Opcode::OpNot),
        ("--" , Opcode::OpDec),
        ("++" , Opcode::OpInc),
        ("}"  , Opcode::OpCl)
    ]
}

fn init_regset() -> HashMap<&'static str, u8> {
    // Use from sdb later, probably a better option.
    hash![
        ("rax", 64),
        ("rbx", 64),
        ("rcx", 64),
        ("rdx", 64),
        ("rsp", 64),
        ("rbp", 64),
        ("rsi", 64),
        ("rdi", 64),
        ("rip", 64),
        ("zf",   1)
    ]
}

pub struct Parser<'a> {
    stack: Vec<Value>,
    insts: Vec<Instruction>,
    opset: HashMap<&'a str, Opcode>,
    regset: HashMap<&'a str, u8>,
    tmp_index: u64,
    default_size: u8,
    // The address the parser is currently parsing at.
    addr: Address,
    // Name of the Instruction pointer for the architecture.
    ip: String,
}

impl<'a> Parser<'a> {
    pub fn new() -> Parser<'a> {
        Parser {
            stack: Vec::new(),
            insts: Vec::new(),
            opset: map_esil_to_opset(),
            regset: init_regset(),
            tmp_index: 0,
            // TODO: change this default based on arch.
            default_size: 64,
            addr: 0,
            // TODO: Set dynamically based on the arch.
            ip: "rip".to_string(),
        }
    }

    fn get_tmp_register(&mut self, mut size: u8) -> Value {
        self.tmp_index += 1;
        if size == 0 {
            size = self.default_size;
        }
        Value::tmp(self.tmp_index, size)
    }

    fn add_widen_inst(&mut self, op: &mut Value, size: u8) {
        if op.size > size {
            return;
        }
        let dst = self.get_tmp_register(size);
        let operator = Opcode::OpWiden;
        self.insts.push(Instruction::new(operator, dst.clone(), op.clone(), Value::constant(size as i64), Some(self.addr)));
        *op = dst;
    }

    fn add_narrow_inst(&mut self, op: &mut Value, size: u8) {
        if op.size < size {
            return;
        }
        let dst = self.get_tmp_register(size);
        let operator = Opcode::OpNarrow;
        self.insts.push(Instruction::new(operator, dst.clone(), op.clone(), Value::constant(size as i64), Some(self.addr)));
        *op = dst;
    }

    fn add_assign_inst(&mut self, op: Opcode) -> Result<(), ParseError> {
        let dst = match self.stack.pop() {
            Some(ele) => ele,
            None => return Err(ParseError::InsufficientOperands),
        };

        let mut op1 = match self.stack.pop() {
            Some(ele) => ele,
            None => return Err(ParseError::InsufficientOperands),
        };

        // If it is an assignment to the Instruction Pointer, then the Instruction should be a OpJmp
        // rather than an OpEq.
        if dst.name == self.ip {
            self.insts.push(Instruction::new(Opcode::OpJmp, Value::null(), op1, Value::null(), Some(self.addr)));
            return Ok(());
        }

        if dst.size == op1.size {
            self.insts.push(Instruction::new(op, dst.clone(), op1, Value::null(), Some(self.addr)));
            return Ok(());
        }

        if dst.size > op1.size {
            self.add_widen_inst(&mut op1, dst.size);
        } else {
            self.add_narrow_inst(&mut op1, dst.size);
        }

        // We don't need to use another instruction for assignment. Just replace the dst of the
        // narrow/widen instruction generated.
        self.insts.last_mut().unwrap().dst = dst.clone();
        Ok(())
    }

    fn add_inst(&mut self, op: Opcode) -> Result<(), ParseError> {
        // Handle "}".
        if op == Opcode::OpCl {
            let null = Value::null();
            self.insts.push(Instruction::new(op, null.clone(), null.clone(), null.clone(), Some(self.addr)));
            return Ok(());
        }

        // Assignment operation has to be handled quite differently.
        if op == Opcode::OpEq {
            return self.add_assign_inst(op);
        }

        let mut op2 = match self.stack.pop() {
            Some(ele) => ele,
            None => return Err(ParseError::InsufficientOperands),
        };

        let mut op1 = Value::null();
        if op.to_operator().arity == Arity::Binary {
            op1 = match self.stack.pop() {
                Some(ele) => ele,
                None => return Err(ParseError::InsufficientOperands),
            };
        }

        if op == Opcode::OpIf {
            self.insts.push(Instruction::new(op, Value::null(), op2, op1, Some(self.addr)));
            return Ok(());
        }

        let mut dst_size: u8;
        let mut dst: Value;
        dst_size = cmp::max(op1.size, op2.size);
        dst = self.get_tmp_register(dst_size);

        // Add a check to see if dst, op1 and op2 have the same size.
        // If they do not, cast it. op2 is never 'Null'.
        assert!(op2.location != Location::Null);

        if op.to_operator().arity == Arity::Binary {
            if op1.size > op2.size {
                dst_size = op1.size;
                self.add_widen_inst(&mut op2, op1.size);
            } else if op2.size > op1.size {
                dst_size = op2.size;
                self.add_widen_inst(&mut op1, op2.size);
            }
        }

        dst.size = dst_size;

        self.insts.push(Instruction::new(op, dst.clone(), op2, op1, Some(self.addr)));
        self.stack.push(dst);

        Ok(())
    }

    pub fn parse(&mut self, esil: String, _addr: Option<u64>) -> Result<(), ParseError> {
        // Set parser address.
        self.addr = match _addr {
            Some(s) => {
                s
            },
            None => self.addr + 1,
        };

        let esil: Vec<String> = esil.split(',')
                                    .map(|x| x.to_string()).collect();
        for token in esil {
            let op = match self.opset.get(&*token) {
                Some(op) => op.clone(),
                None => Opcode::OpInvalid,
            };

            if op != Opcode::OpInvalid {
                try!(self.add_inst(op));
                continue;
            }

            // If it contains atleast one alpha, it cannot be an operator.
            let re = Regex::new("[a-zA-Z]").unwrap();
            if re.is_match(&*token) {
                let mut val_type = Location::Unknown;
                let mut val: i64 = 0;
                let mut size: u8 = self.default_size;
                if let Some(r) = self.regset.get(&*token) {
                    val_type = Location::Register;
                    // For now, reg is just a u8.
                    size = *r;
                } else if let Ok(v) = token.parse::<i64>() {
                    val_type = Location::Constant;
                    val = v;
                } else if let Ok(v) = hex_to_i!(token) {
                    val_type = Location::Constant;
                    val = v;
                }
                let v = Value::new(String::from(token), size, val_type, val, 0);
                self.stack.push(v);
                continue;
            }

            // Handle constants.
            if let Ok(num) = token.parse::<i64>() {
                let val_type = Location::Constant;
                let val = num;
                let size  = self.default_size;
                let name = format!("0x{:x}", num);
                let v = Value::new(name, size, val_type, val, 0);
                self.stack.push(v);
                continue;
            }

            // Deal with normal 'composite' instructions.
            if token.char_indices().last().unwrap().1 != ']' {
                let mut dst: Value;
                if let Some(x) = self.stack.last() {
                    dst = x.clone();
                } else {
                    return Err(ParseError::InsufficientOperands);
                }
                let re = Regex::new(r"^(.|..)=$").unwrap();
                let t = re.captures(&*token).unwrap().at(1).unwrap_or("");
                if t.len() == 0 {
                    return Err(ParseError::InvalidOperator);
                }
                let op = match self.opset.get(t) {
                    Some(op) => op.clone(),
                    None => return Err(ParseError::InvalidOperator),
                };

                try!(self.add_inst(op));
                self.stack.push(dst);
                try!(self.add_inst(Opcode::OpEq));
                continue;
            }

            // Deal with memaccess 'composite' instructions.
            let re = Regex::new(r"^(.|..)?(=)?\[([1248]?)\]$").unwrap();
            let tokens = re.captures(&*token).unwrap();
            let eq = tokens.at(2).unwrap_or("");
            let has_op = tokens.at(1).unwrap_or("");
            let access_size = tokens.at(3).unwrap_or("");
            let access_size = match access_size {
                "" => self.default_size,
                _ => access_size.parse::<u8>().unwrap() * 8,
            };

            try!(self.add_inst(Opcode::OpRef));
            // Set the correct size.
            let mut x = self.stack.pop().unwrap();
            self.add_narrow_inst(&mut x, access_size);
            let tmp_dst1 = x.clone();
            self.stack.push(x);

            // Simple 'peek' ([n])
            if eq.is_empty() {
                continue;
            }

            // Simple 'poke' (=[n])
            if has_op.is_empty() {
                try!(self.add_inst(Opcode::OpEq));
                continue;
            }

            // 'poke' with another operation. (<op>=[n])
            let o = match self.opset.get(has_op) {
                Some(x) => x.clone(),
                // Return with error
                None => return Err(ParseError::InvalidOperator),
            };
            try!(self.add_inst(o));
            // Reassignment.
            self.stack.push(tmp_dst1);
            try!(self.add_inst(Opcode::OpEq));
        }
        Ok(())
    }

    /// Emit-instructions converts certain compound instructions to simpler instructions.
    /// For example, the sequence of instructions,
    /// ```none
    /// if zf            (OpIf)
    ///   jump addr      (OpJmp)
    /// ```
    /// is converted to a single conditional jump as,
    /// ```none
    /// if zf jump addr  (OpCJmp)
    /// ```

    pub fn emit_insts(&mut self) -> Vec<Instruction> {
        let len = self.insts.len();
        let mut res: Vec<Instruction> = Vec::new();
        let mut i = 0;
        while i < len {
            let inst = self.insts[i].clone();
            if inst.opcode != Opcode::OpIf {
                res.push(inst);
                i += 1;
                continue;
            }

            // Note(sushant94): We need to improve this process if we want to retain the side-effects.
            // For example, a x86 call instruction will result the corresponding esil statement to
            // have instructions to set esp and then jump to the required location.
            // We can use these side-effects to determine if it's a 'call' or just a jump.
            // This however requires study into other architectures and their esil too.
            while self.insts[i].opcode != Opcode::OpCl && i < len - 1 {
                i += 1;
                let inst_ = self.insts[i].clone();
                if inst_.opcode == Opcode::OpJmp {
                    let res_inst =
                        Instruction::new(Opcode::OpCJmp, Value::null(),
                                         inst.operand_1.clone(),
                                         inst_.operand_1, Some(inst.addr));
                    res.push(res_inst);
                }
            }
            i += 1;
        }
        self.insts = res;
        return (self).insts.clone();
    }
}

#[test]
fn testing() {
    let mut p = Parser::new();
    p.parse(String::from("0,0x204db1,rip,+,[1],==,%z,zf,=,%b8,cf,=,%p,pf,=,%s,sf,="), None);
}
<|endoftext|>


<commit_before>extern crate space_colonization;
extern crate nalgebra as na;
extern crate rand;
extern crate num;
extern crate clap;
extern crate eps_writer;

use na::{Pnt2, Vec2, FloatPnt, FloatVec};
use num::Zero;
use space_colonization::{SpaceColonization, SqDist};
use eps_writer::{EpsDocument, Position, Points, Lines, SetRGB};
use std::fs::File;
use common::{MyPoint, Config};
use std::fmt::Debug;

pub mod common;

const SCALE: f32 = 400.0;

fn run<T, F>(config: &Config)
    where T: MyPoint + FloatPnt<f32, F> + Debug,
          F: FloatVec<f32> + Zero + Copy + Debug
{
    let mut rng = rand::thread_rng();

    let mut sc: SpaceColonization<T, F, ()> =
        SpaceColonization::new(SqDist::from_dist(config.influence_radius),
                               SqDist::from_dist(config.kill_distance),
                               100, // max_length
                               2, // max_branches
                               config.move_distance);

    for _ in 0..config.n_roots {
        sc.add_root_node(<T as MyPoint>::random(&mut rng));
    }

    for _ in 0..config.n_attraction_points {
        sc.add_default_attractor(<T as MyPoint>::random(&mut rng));
    }

    let mut i = 0;

    loop {
        if let Some(n) = config.save_every {
            // save current iteration as eps
            if i % n == 0 {
                let filename = format!("out_{:05}.eps", i);
                let mut document = EpsDocument::new();

                let mut points = Vec::new();
                sc.visit_attractor_points(&mut|position| {
                                           let pnt = position.into_pnt3();
                                           points.push(Position::new(pnt.x, pnt.y));
                                       });

                document.add_shape(Box::new(Points(points, 0.005 * SCALE / 2.0)));

                let mut lines = Vec::new();
                sc.visit_node_segments(&mut |&a, &b| {
                    let pt1 = a.into_pnt3();
                    let pt2 = b.into_pnt3();
                    lines.push((Position::new(pt1.x, pt1.y), Position::new(pt2.x, pt2.y)));
                });
                document.add_shape(Box::new(SetRGB(1.0, 0.0, 0.0)));
                document.add_shape(Box::new(Lines(lines)));

                document.transform(Vec2::new(1.0, 1.0), Vec2::new(SCALE / 2.0, SCALE / 2.0));

                let mut file = File::create(filename).unwrap();
                document.write_eps(&mut file, 100.0, 100.0).unwrap();
            }
        }

        if let Some(m) = config.max_iter {
            if i > m {
                break;
            }
        }

        let new_nodes = sc.next();

        println!("Iteration: {}. New nodes: {:?}", i, new_nodes);

        i += 1;
    }
}

fn main() {
    let config = Config::from_cmd();

    println!("{:?}", config);
    assert!(config.use_3d == false);
    run::<Pnt2<f32>, Vec2<f32>>(&config);
}
<commit_msg>Use config<commit_after>extern crate space_colonization;
extern crate nalgebra as na;
extern crate rand;
extern crate num;
extern crate clap;
extern crate eps_writer;

use na::{Pnt2, Vec2, FloatPnt, FloatVec};
use num::Zero;
use space_colonization::{SpaceColonization, SqDist};
use eps_writer::{EpsDocument, Position, Points, Lines, SetRGB};
use std::fs::File;
use common::{MyPoint, Config};
use std::fmt::Debug;

pub mod common;

const SCALE: f32 = 400.0;

fn run<T, F>(config: &Config)
    where T: MyPoint + FloatPnt<f32, F> + Debug,
          F: FloatVec<f32> + Zero + Copy + Debug
{
    let mut rng = rand::thread_rng();

    let mut sc: SpaceColonization<T, F, ()> =
        SpaceColonization::new(SqDist::from_dist(config.influence_radius),
                               SqDist::from_dist(config.kill_distance),
                               config.max_length,
                               config.max_branches,
                               config.move_distance);

    for _ in 0..config.n_roots {
        sc.add_root_node(<T as MyPoint>::random(&mut rng));
    }

    for _ in 0..config.n_attraction_points {
        sc.add_default_attractor(<T as MyPoint>::random(&mut rng));
    }

    let mut i = 0;

    loop {
        if let Some(n) = config.save_every {
            // save current iteration as eps
            if i % n == 0 {
                let filename = format!("out_{:05}.eps", i);
                let mut document = EpsDocument::new();

                let mut points = Vec::new();
                sc.visit_attractor_points(&mut|position| {
                                           let pnt = position.into_pnt3();
                                           points.push(Position::new(pnt.x, pnt.y));
                                       });

                document.add_shape(Box::new(Points(points, 0.005 * SCALE / 2.0)));

                let mut lines = Vec::new();
                sc.visit_node_segments(&mut |&a, &b| {
                    let pt1 = a.into_pnt3();
                    let pt2 = b.into_pnt3();
                    lines.push((Position::new(pt1.x, pt1.y), Position::new(pt2.x, pt2.y)));
                });
                document.add_shape(Box::new(SetRGB(1.0, 0.0, 0.0)));
                document.add_shape(Box::new(Lines(lines)));

                document.transform(Vec2::new(1.0, 1.0), Vec2::new(SCALE / 2.0, SCALE / 2.0));

                let mut file = File::create(filename).unwrap();
                document.write_eps(&mut file, 100.0, 100.0).unwrap();
            }
        }

        if let Some(m) = config.max_iter {
            if i > m {
                break;
            }
        }

        let new_nodes = sc.next();

        println!("Iteration: {}. New nodes: {:?}", i, new_nodes);

        i += 1;
    }
}

fn main() {
    let config = Config::from_cmd();

    println!("{:?}", config);
    assert!(config.use_3d == false);
    run::<Pnt2<f32>, Vec2<f32>>(&config);
}
<|endoftext|>