<commit_before>use std::collections::HashMap;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use std::iter::FromIterator;

use common::Solution;

type State = Vec<(i64, bool)>;

#[derive(Default, Debug)]
pub struct Day12 {
    productions: [bool; 32],
}

fn char_bool(c: char) -> bool {
    match c {
        '#' => true,
        '.' => false,
        _ => panic!("Invalid input {}", c)
    }
}

fn print_state(state: &State) -> String {
    let mut buf = String::with_capacity(state.len());
    for &(_, b) in state {
        if b {
            buf.push('#');
        } else {
            buf.push('.');
        }
    }
    buf
}

fn state_from_string(representation: &str, offset: i64) -> State {
    Vec::from_iter(representation.chars()
        .enumerate()
        .map(|(i, c)| (i as i64 + offset, char_bool(c))))
}

impl Day12 {
    pub fn new() -> Self {
        Default::default()
    }

    fn read_input(&mut self, input: &mut Read) -> State {
        let state;
        let mut reader = BufReader::new(input);
        {
            let mut line = String::new();
            reader.read_line(&mut line).unwrap();
            state = state_from_string(&line["initial state:".len()..line.len()].trim(), 0);
        }

        for line in reader.lines() {
            let line = line.unwrap();

            if line.is_empty() {
                continue;
            }

            let mut index = 0;
            for c in line.chars().take(5) {
                index <<= 1;
                if char_bool(c) {
                    index |= 1;
                }
            }

            self.productions[index] = char_bool(line.chars().last().unwrap());
        }

        state
    }

    fn simulate(&self, state: &State) -> State {
        let mut new_state = Vec::with_capacity(state.len() + 8);
        let mut index = 0;
        let mut last_idx = None;
        for &(idx, b) in state {
            index = (index << 1) & 0x1f;
            if b {
                index |= 1;
            }

            if self.productions[index] {
                new_state.push((idx - 2, true));
            } else if !new_state.is_empty() {
                new_state.push((idx - 2, false));
            }
            last_idx = Some(idx);
        }
        if let Some(idx) = last_idx {
            for i in 1..=4 {
                index = (index << 1) & 0x1f;

                if self.productions[index] {
                    new_state.push((idx + i - 2, true));
                } else if !new_state.is_empty() {
                    new_state.push((idx + i - 2, false));
                }
            }
        }

        let new_len = new_state.len() - new_state.iter().rev()
            .take_while(|&&(_, x)| !x).count();
        new_state.truncate(new_len);

        new_state
    }

    fn simulate_n(&self, mut state: State, n: usize) -> State {
        for _ in 0..n {
            state = self.simulate(&state);
        }
        state
    }

    fn sum(&self, state: &State) -> i64 {
        state.iter()
            .filter(|&&(_, x)| x)
            .map(|&(i, _)| i)
            .sum()
    }
}

impl Solution for Day12 {
    fn part1(&mut self, input: &mut Read) -> String {
        let mut state = self.read_input(input);
        state = self.simulate_n(state, 20);

        format!("{}", self.sum(&state))
    }

    fn part2(&mut self, input: &mut Read) -> String {
        // Note: this is way too slow
        let mut state = self.read_input(input);
        let mut seen = HashMap::new();
        let mut time = 1i64;
        const TARGET_TIME: i64 = 50000000000;

        loop {
            state = self.simulate(&state);
            let &(offset, _) = state.first().unwrap();
            let representation = print_state(&state);
            if let Some((o_offset, o_time)) = seen.get(&representation) {
                let remaining_time = TARGET_TIME - o_time;
                let cycle_length = time - o_time;
                let iterations_skipped = remaining_time / cycle_length;
                let remainder = remaining_time % cycle_length;
                let new_offset = o_offset + iterations_skipped * (offset - o_offset);

                state = state_from_string(&representation, new_offset);
                state = self.simulate_n(state, remainder as usize);

                return format!("{}", self.sum(&state));
            }
            seen.insert(representation, (offset, time));
            time += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use common::Solution;
    use day12::Day12;

    const SAMPLE_INPUT: &[u8] = include_bytes!("samples/12.txt");

    #[test]
    fn sample_part1() {
        let mut instance = Day12::new();
        assert_eq!("325", instance.part1(&mut SAMPLE_INPUT));
    }
}
<commit_msg>Deduplicate day 12.<commit_after>use std::collections::HashMap;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use std::iter::FromIterator;

use common::Solution;

type State = Vec<(i64, bool)>;

#[derive(Default, Debug)]
pub struct Day12 {
    productions: [bool; 32],
}

fn char_bool(c: char) -> bool {
    match c {
        '#' => true,
        '.' => false,
        _ => panic!("Invalid input {}", c)
    }
}

fn print_state(state: &State) -> String {
    let mut buf = String::with_capacity(state.len());
    for &(_, b) in state {
        if b {
            buf.push('#');
        } else {
            buf.push('.');
        }
    }
    buf
}

fn state_from_string(representation: &str, offset: i64) -> State {
    Vec::from_iter(representation.chars()
        .enumerate()
        .map(|(i, c)| (i as i64 + offset, char_bool(c))))
}

impl Day12 {
    pub fn new() -> Self {
        Default::default()
    }

    fn read_input(&mut self, input: &mut Read) -> State {
        let state;
        let mut reader = BufReader::new(input);
        {
            let mut line = String::new();
            reader.read_line(&mut line).unwrap();
            state = state_from_string(&line["initial state:".len()..line.len()].trim(), 0);
        }

        for line in reader.lines() {
            let line = line.unwrap();

            if line.is_empty() {
                continue;
            }

            let mut index = 0;
            for c in line.chars().take(5) {
                index <<= 1;
                if char_bool(c) {
                    index |= 1;
                }
            }

            self.productions[index] = char_bool(line.chars().last().unwrap());
        }

        state
    }

    fn simulate(&self, state: &State) -> State {
        let mut new_state = Vec::with_capacity(state.len() + 8);
        let mut index = 0;
        for &(idx, b) in state {
            index = self.advance_state(&mut new_state, index, idx, b)
        }
        if let Some((idx, _)) = state.last() {
            for i in 1..=4 {
                index = self.advance_state(&mut new_state, index, idx + i, false);
            }
        }

        let new_len = new_state.len() - new_state.iter().rev()
            .take_while(|&&(_, x)| !x).count();
        new_state.truncate(new_len);

        new_state
    }

    fn advance_state(&self, new_state: &mut State, mut index: usize, idx: i64, b: bool) -> usize {
        index = (index << 1) & 0x1f | b as usize;
        if self.productions[index] {
            new_state.push((idx - 2, true));
        } else if !new_state.is_empty() {
            new_state.push((idx - 2, false));
        }

        index
    }

    fn simulate_n(&self, mut state: State, n: usize) -> State {
        for _ in 0..n {
            state = self.simulate(&state);
        }
        state
    }

    fn sum(&self, state: &State) -> i64 {
        state.iter()
            .filter(|&&(_, x)| x)
            .map(|&(i, _)| i)
            .sum()
    }
}

impl Solution for Day12 {
    fn part1(&mut self, input: &mut Read) -> String {
        let mut state = self.read_input(input);
        state = self.simulate_n(state, 20);

        format!("{}", self.sum(&state))
    }

    fn part2(&mut self, input: &mut Read) -> String {
        // Note: this is way too slow
        let mut state = self.read_input(input);
        let mut seen = HashMap::new();
        let mut time = 1i64;
        const TARGET_TIME: i64 = 50000000000;

        loop {
            state = self.simulate(&state);
            let &(offset, _) = state.first().unwrap();
            let representation = print_state(&state);
            if let Some((o_offset, o_time)) = seen.get(&representation) {
                let remaining_time = TARGET_TIME - o_time;
                let cycle_length = time - o_time;
                let iterations_skipped = remaining_time / cycle_length;
                let remainder = remaining_time % cycle_length;
                let new_offset = o_offset + iterations_skipped * (offset - o_offset);

                state = state_from_string(&representation, new_offset);
                state = self.simulate_n(state, remainder as usize);

                return format!("{}", self.sum(&state));
            }
            seen.insert(representation, (offset, time));
            time += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use common::Solution;
    use day12::Day12;

    const SAMPLE_INPUT: &[u8] = include_bytes!("samples/12.txt");

    #[test]
    fn sample_part1() {
        let mut instance = Day12::new();
        assert_eq!("325", instance.part1(&mut SAMPLE_INPUT));
    }
}
<|endoftext|>


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

//! Parameterized string expansion

use core::prelude::*;
use core::{char, int, vec};

#[deriving(Eq)]
enum States {
    Nothing,
    Percent,
    SetVar,
    GetVar,
    PushParam,
    CharConstant,
    CharClose,
    IntConstant,
    IfCond,
    IfBody
}

/// Types of parameters a capability can use
pub enum Param {
    String(~str),
    Char(char),
    Number(int)
}

/// Container for static and dynamic variable arrays
pub struct Variables {
    /// Static variables A-Z
    sta: [Param, ..26],
    /// Dynamic variables a-z
    dyn: [Param, ..26]
}

impl Variables {
    /// Return a new zero-initialized Variables
    pub fn new() -> Variables {
        Variables{ sta: [Number(0), ..26], dyn: [Number(0), ..26] }
    }
}

/**
  Expand a parameterized capability

  # Arguments
  * `cap`    - string to expand
  * `params` - vector of params for %p1 etc
  * `vars`   - Variables struct for %Pa etc

  To be compatible with ncurses, `vars` should be the same between calls to `expand` for
  multiple capabilities for the same terminal.
  */
pub fn expand(cap: &[u8], params: &mut [Param], vars: &mut Variables)
    -> Result<~[u8], ~str> {
    assert!(cap.len() != 0, "expanding an empty capability makes no sense");
    assert!(params.len() <= 9, "only 9 parameters are supported by capability strings");

    let mut state = Nothing;
    let mut i = 0;

    // expanded cap will only rarely be smaller than the cap itself
    let mut output = vec::with_capacity(cap.len());

    let mut cur;

    let mut stack: ~[Param] = ~[];

    let mut intstate = ~[];

    while i < cap.len() {
        cur = cap[i] as char;
        let mut old_state = state;
        match state {
            Nothing => {
                if cur == '%' {
                    state = Percent;
                } else {
                    output.push(cap[i]);
                }
            },
            Percent => {
                match cur {
                    '%' => { output.push(cap[i]); state = Nothing },
                    'c' => match stack.pop() {
                        Char(c) => output.push(c as u8),
                        _       => return Err(~"a non-char was used with %c")
                    },
                    's' => match stack.pop() {
                        String(s) => output.push_all(s.as_bytes()),
                        _         => return Err(~"a non-str was used with %s")
                    },
                    'd' => match stack.pop() {
                        Number(x) => {
                            let s = x.to_str();
                            output.push_all(s.as_bytes())
                        }
                        _         => return Err(~"a non-number was used with %d")
                    },
                    'p' => state = PushParam,
                    'P' => state = SetVar,
                    'g' => state = GetVar,
                    '\'' => state = CharConstant,
                    '{' => state = IntConstant,
                    'l' => match stack.pop() {
                        String(s) => stack.push(Number(s.len() as int)),
                        _         => return Err(~"a non-str was used with %l")
                    },
                    '+' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x + y)),
                        (_, _) => return Err(~"non-numbers on stack with +")
                    },
                    '-' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x - y)),
                        (_, _) => return Err(~"non-numbers on stack with -")
                    },
                    '*' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x * y)),
                        (_, _) => return Err(~"non-numbers on stack with *")
                    },
                    '/' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x / y)),
                        (_, _) => return Err(~"non-numbers on stack with /")
                    },
                    'm' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x % y)),
                        (_, _) => return Err(~"non-numbers on stack with %")
                    },
                    '&' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x & y)),
                        (_, _) => return Err(~"non-numbers on stack with &")
                    },
                    '|' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x | y)),
                        (_, _) => return Err(~"non-numbers on stack with |")
                    },
                    'A' => match (stack.pop(), stack.pop()) {
                        (Number(0), Number(_)) => stack.push(Number(0)),
                        (Number(_), Number(0)) => stack.push(Number(0)),
                        (Number(_), Number(_)) => stack.push(Number(1)),
                        _ => return Err(~"non-numbers on stack with logical and")
                    },
                    'O' => match (stack.pop(), stack.pop()) {
                        (Number(0), Number(0)) => stack.push(Number(0)),
                        (Number(_), Number(_)) => stack.push(Number(1)),
                        _ => return Err(~"non-numbers on stack with logical or")
                    },
                    '!' => match stack.pop() {
                        Number(0) => stack.push(Number(1)),
                        Number(_) => stack.push(Number(0)),
                        _ => return Err(~"non-number on stack with logical not")
                    },
                    '~' => match stack.pop() {
                        Number(x) => stack.push(Number(!x)),
                        _         => return Err(~"non-number on stack with %~")
                    },
                    'i' => match (copy params[0], copy params[1]) {
                        (Number(ref mut x), Number(ref mut y)) => {
                            *x += 1;
                            *y += 1;
                        },
                        (_, _) => return Err(~"first two params not numbers with %i")
                    },
                    '?' => state = return Err(fmt!("if expressions unimplemented (%?)", cap)),
                    _ => return Err(fmt!("unrecognized format option %c", cur))
                }
            },
            PushParam => {
                // params are 1-indexed
                stack.push(copy params[char::to_digit(cur, 10).expect("bad param number") - 1]);
            },
            SetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    vars.sta[idx] = stack.pop();
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    vars.dyn[idx] = stack.pop();
                } else {
                    return Err(~"bad variable name in %P");
                }
            },
            GetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    stack.push(copy vars.sta[idx]);
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    stack.push(copy vars.dyn[idx]);
                } else {
                    return Err(~"bad variable name in %g");
                }
            },
            CharConstant => {
                stack.push(Char(cur));
                state = CharClose;
            },
            CharClose => {
                if cur != '\'' {
                    return Err(~"malformed character constant");
                }
            },
            IntConstant => {
                if cur == '}' {
                    stack.push(Number(int::parse_bytes(intstate, 10).expect("bad int constant")));
                    state = Nothing;
                }
                intstate.push(cur as u8);
                old_state = Nothing;
            }
            _ => return Err(~"unimplemented state")
        }
        if state == old_state {
            state = Nothing;
        }
        i += 1;
    }
    Ok(output)
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_basic_setabf() {
        let s = bytes!("\\E[48;5;%p1%dm");
        assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(), bytes!("\\E[48;5;1m").to_owned());
    }
}
<commit_msg>Don't require &mut [Param] with terminfo::parm::expand()<commit_after>// Copyright 2012 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.

//! Parameterized string expansion

use core::prelude::*;
use core::{char, int, vec};
use core::iterator::IteratorUtil;

#[deriving(Eq)]
enum States {
    Nothing,
    Percent,
    SetVar,
    GetVar,
    PushParam,
    CharConstant,
    CharClose,
    IntConstant,
    IfCond,
    IfBody
}

/// Types of parameters a capability can use
pub enum Param {
    String(~str),
    Char(char),
    Number(int)
}

/// Container for static and dynamic variable arrays
pub struct Variables {
    /// Static variables A-Z
    sta: [Param, ..26],
    /// Dynamic variables a-z
    dyn: [Param, ..26]
}

impl Variables {
    /// Return a new zero-initialized Variables
    pub fn new() -> Variables {
        Variables{ sta: [Number(0), ..26], dyn: [Number(0), ..26] }
    }
}

/**
  Expand a parameterized capability

  # Arguments
  * `cap`    - string to expand
  * `params` - vector of params for %p1 etc
  * `vars`   - Variables struct for %Pa etc

  To be compatible with ncurses, `vars` should be the same between calls to `expand` for
  multiple capabilities for the same terminal.
  */
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
    -> Result<~[u8], ~str> {
    let mut state = Nothing;
    let mut i = 0;

    // expanded cap will only rarely be larger than the cap itself
    let mut output = vec::with_capacity(cap.len());

    let mut cur;

    let mut stack: ~[Param] = ~[];

    let mut intstate = ~[];

    // Copy parameters into a local vector for mutability
    let mut mparams = [Number(0), ..9];
    for mparams.mut_iter().zip(params.iter()).advance |(dst, &src)| {
        *dst = src;
    }

    while i < cap.len() {
        cur = cap[i] as char;
        let mut old_state = state;
        match state {
            Nothing => {
                if cur == '%' {
                    state = Percent;
                } else {
                    output.push(cap[i]);
                }
            },
            Percent => {
                match cur {
                    '%' => { output.push(cap[i]); state = Nothing },
                    'c' => match stack.pop() {
                        Char(c) => output.push(c as u8),
                        _       => return Err(~"a non-char was used with %c")
                    },
                    's' => match stack.pop() {
                        String(s) => output.push_all(s.as_bytes()),
                        _         => return Err(~"a non-str was used with %s")
                    },
                    'd' => match stack.pop() {
                        Number(x) => {
                            let s = x.to_str();
                            output.push_all(s.as_bytes())
                        }
                        _         => return Err(~"a non-number was used with %d")
                    },
                    'p' => state = PushParam,
                    'P' => state = SetVar,
                    'g' => state = GetVar,
                    '\'' => state = CharConstant,
                    '{' => state = IntConstant,
                    'l' => match stack.pop() {
                        String(s) => stack.push(Number(s.len() as int)),
                        _         => return Err(~"a non-str was used with %l")
                    },
                    '+' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x + y)),
                        (_, _) => return Err(~"non-numbers on stack with +")
                    },
                    '-' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x - y)),
                        (_, _) => return Err(~"non-numbers on stack with -")
                    },
                    '*' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x * y)),
                        (_, _) => return Err(~"non-numbers on stack with *")
                    },
                    '/' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x / y)),
                        (_, _) => return Err(~"non-numbers on stack with /")
                    },
                    'm' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x % y)),
                        (_, _) => return Err(~"non-numbers on stack with %")
                    },
                    '&' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x & y)),
                        (_, _) => return Err(~"non-numbers on stack with &")
                    },
                    '|' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x | y)),
                        (_, _) => return Err(~"non-numbers on stack with |")
                    },
                    'A' => match (stack.pop(), stack.pop()) {
                        (Number(0), Number(_)) => stack.push(Number(0)),
                        (Number(_), Number(0)) => stack.push(Number(0)),
                        (Number(_), Number(_)) => stack.push(Number(1)),
                        _ => return Err(~"non-numbers on stack with logical and")
                    },
                    'O' => match (stack.pop(), stack.pop()) {
                        (Number(0), Number(0)) => stack.push(Number(0)),
                        (Number(_), Number(_)) => stack.push(Number(1)),
                        _ => return Err(~"non-numbers on stack with logical or")
                    },
                    '!' => match stack.pop() {
                        Number(0) => stack.push(Number(1)),
                        Number(_) => stack.push(Number(0)),
                        _ => return Err(~"non-number on stack with logical not")
                    },
                    '~' => match stack.pop() {
                        Number(x) => stack.push(Number(!x)),
                        _         => return Err(~"non-number on stack with %~")
                    },
                    'i' => match (copy mparams[0], copy mparams[1]) {
                        (Number(ref mut x), Number(ref mut y)) => {
                            *x += 1;
                            *y += 1;
                        },
                        (_, _) => return Err(~"first two params not numbers with %i")
                    },
                    '?' => state = return Err(fmt!("if expressions unimplemented (%?)", cap)),
                    _ => return Err(fmt!("unrecognized format option %c", cur))
                }
            },
            PushParam => {
                // params are 1-indexed
                stack.push(copy mparams[char::to_digit(cur, 10).expect("bad param number") - 1]);
            },
            SetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    vars.sta[idx] = stack.pop();
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    vars.dyn[idx] = stack.pop();
                } else {
                    return Err(~"bad variable name in %P");
                }
            },
            GetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    stack.push(copy vars.sta[idx]);
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    stack.push(copy vars.dyn[idx]);
                } else {
                    return Err(~"bad variable name in %g");
                }
            },
            CharConstant => {
                stack.push(Char(cur));
                state = CharClose;
            },
            CharClose => {
                if cur != '\'' {
                    return Err(~"malformed character constant");
                }
            },
            IntConstant => {
                if cur == '}' {
                    stack.push(Number(int::parse_bytes(intstate, 10).expect("bad int constant")));
                    state = Nothing;
                }
                intstate.push(cur as u8);
                old_state = Nothing;
            }
            _ => return Err(~"unimplemented state")
        }
        if state == old_state {
            state = Nothing;
        }
        i += 1;
    }
    Ok(output)
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_basic_setabf() {
        let s = bytes!("\\E[48;5;%p1%dm");
        assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(), bytes!("\\E[48;5;1m").to_owned());
    }
}
<|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.

use core::prelude::*;

use boxed::Box;
use cmp;
use mem;
use ptr;
use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
use libc;
use thunk::Thunk;
use ffi::CString;

use sys_common::stack::RED_ZONE;
use sys_common::thread::*;

pub type rust_thread = libc::pthread_t;
pub type rust_thread_return = *mut u8;
pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return;

#[no_stack_check]
pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return {
    return start_thread(main);
}

#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
pub mod guard {
    pub unsafe fn current() -> uint {
        0
    }

    pub unsafe fn main() -> uint {
        0
    }

    pub unsafe fn init() {
    }
}

#[cfg(any(target_os = "linux", target_os = "macos"))]
pub mod guard {
    use super::*;
    #[cfg(any(target_os = "linux", target_os = "android"))]
    use mem;
    #[cfg(any(target_os = "linux", target_os = "android"))]
    use ptr;
    use libc;
    use libc::funcs::posix88::mman::{mmap};
    use libc::consts::os::posix88::{PROT_NONE,
                                    MAP_PRIVATE,
                                    MAP_ANON,
                                    MAP_FAILED,
                                    MAP_FIXED};

    // These are initialized in init() and only read from after
    static mut PAGE_SIZE: uint = 0;
    static mut GUARD_PAGE: uint = 0;

    #[cfg(target_os = "macos")]
    unsafe fn get_stack_start() -> *mut libc::c_void {
        current() as *mut libc::c_void
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    unsafe fn get_stack_start() -> *mut libc::c_void {
        let mut attr: libc::pthread_attr_t = mem::zeroed();
        if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
            panic!("failed to get thread attributes");
        }
        let mut stackaddr = ptr::null_mut();
        let mut stacksize = 0;
        if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
            panic!("failed to get stack information");
        }
        if pthread_attr_destroy(&mut attr) != 0 {
            panic!("failed to destroy thread attributes");
        }
        stackaddr
    }

    pub unsafe fn init() {
        let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
        if psize == -1 {
            panic!("failed to get page size");
        }

        PAGE_SIZE = psize as uint;

        let mut stackaddr = get_stack_start();

        // Ensure stackaddr is page aligned! A parent process might
        // have reset RLIMIT_STACK to be non-page aligned. The
        // pthread_attr_getstack() reports the usable stack area
        // stackaddr < stackaddr + stacksize, so if stackaddr is not
        // page-aligned, calculate the fix such that stackaddr <
        // new_page_aligned_stackaddr < stackaddr + stacksize
        let remainder = (stackaddr as usize) % (PAGE_SIZE as usize);
        if remainder != 0 {
            stackaddr = ((stackaddr as usize) + (PAGE_SIZE as usize) - remainder)
                as *mut libc::c_void;
        }

        // Rellocate the last page of the stack.
        // This ensures SIGBUS will be raised on
        // stack overflow.
        let result = mmap(stackaddr,
                          PAGE_SIZE as libc::size_t,
                          PROT_NONE,
                          MAP_PRIVATE | MAP_ANON | MAP_FIXED,
                          -1,
                          0);

        if result != stackaddr || result == MAP_FAILED {
            panic!("failed to allocate a guard page");
        }

        let offset = if cfg!(target_os = "linux") {
            2
        } else {
            1
        };

        GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
    }

    pub unsafe fn main() -> uint {
        GUARD_PAGE
    }

    #[cfg(target_os = "macos")]
    pub unsafe fn current() -> uint {
        (pthread_get_stackaddr_np(pthread_self()) as libc::size_t -
         pthread_get_stacksize_np(pthread_self())) as uint
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    pub unsafe fn current() -> uint {
        let mut attr: libc::pthread_attr_t = mem::zeroed();
        if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
            panic!("failed to get thread attributes");
        }
        let mut guardsize = 0;
        if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
            panic!("failed to get stack guard page");
        }
        if guardsize == 0 {
            panic!("there is no guard page");
        }
        let mut stackaddr = ptr::null_mut();
        let mut stacksize = 0;
        if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
            panic!("failed to get stack information");
        }
        if pthread_attr_destroy(&mut attr) != 0 {
            panic!("failed to destroy thread attributes");
        }

        stackaddr as uint + guardsize as uint
    }
}

pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
    let mut native: libc::pthread_t = mem::zeroed();
    let mut attr: libc::pthread_attr_t = mem::zeroed();
    assert_eq!(pthread_attr_init(&mut attr), 0);
    assert_eq!(pthread_attr_setdetachstate(&mut attr,
                                           PTHREAD_CREATE_JOINABLE), 0);

    // Reserve room for the red zone, the runtime's stack of last resort.
    let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
    match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
        0 => {
        },
        libc::EINVAL => {
            // EINVAL means |stack_size| is either too small or not a
            // multiple of the system page size.  Because it's definitely
            // >= PTHREAD_STACK_MIN, it must be an alignment issue.
            // Round up to the nearest page and try again.
            let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint;
            let stack_size = (stack_size + page_size - 1) &
                             (-(page_size as int - 1) as uint - 1);
            assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
        },
        errno => {
            // This cannot really happen.
            panic!("pthread_attr_setstacksize() error: {}", errno);
        },
    };

    let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint
    let ret = pthread_create(&mut native, &attr, thread_start, arg);
    assert_eq!(pthread_attr_destroy(&mut attr), 0);

    if ret != 0 {
        // be sure to not leak the closure
        let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg);
        panic!("failed to spawn native thread: {}", ret);
    }
    native
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub unsafe fn set_name(name: &str) {
    // pthread_setname_np() since glibc 2.12
    // availability autodetected via weak linkage
    let cname = CString::from_slice(name.as_bytes());
    type F = unsafe extern "C" fn(libc::pthread_t, *const libc::c_char) -> libc::c_int;
    extern {
        #[linkage = "extern_weak"]
        static pthread_setname_np: *const ();
    }
    if !pthread_setname_np.is_null() {
        unsafe {
            mem::transmute::<*const (), F>(pthread_setname_np)(pthread_self(), cname.as_ptr());
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub unsafe fn set_name(name: &str) {
    // pthread_set_name_np() since almost forever on all BSDs
    let cname = CString::from_slice(name.as_bytes());
    pthread_set_name_np(pthread_self(), cname.as_ptr());
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub unsafe fn set_name(name: &str) {
    // pthread_setname_np() since OS X 10.6 and iOS 3.2
    let cname = CString::from_slice(name.as_bytes());
    pthread_setname_np(cname.as_ptr());
}

pub unsafe fn join(native: rust_thread) {
    assert_eq!(pthread_join(native, ptr::null_mut()), 0);
}

pub unsafe fn detach(native: rust_thread) {
    assert_eq!(pthread_detach(native), 0);
}

pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
// glibc >= 2.15 has a __pthread_get_minstack() function that returns
// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
// storage.  We need that information to avoid blowing up when a small stack
// is created in an application with big thread-local storage requirements.
// See #6233 for rationale and details.
//
// Link weakly to the symbol for compatibility with older versions of glibc.
// Assumes that we've been dynamically linked to libpthread but that is
// currently always the case.  Note that you need to check that the symbol
// is non-null before calling it!
#[cfg(target_os = "linux")]
fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t {
    type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t;
    extern {
        #[linkage = "extern_weak"]
        static __pthread_get_minstack: *const ();
    }
    if __pthread_get_minstack.is_null() {
        PTHREAD_STACK_MIN
    } else {
        unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) }
    }
}

// __pthread_get_minstack() is marked as weak but extern_weak linkage is
// not supported on OS X, hence this kludge...
#[cfg(not(target_os = "linux"))]
fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t {
    PTHREAD_STACK_MIN
}

#[cfg(any(target_os = "linux"))]
extern {
    pub fn pthread_self() -> libc::pthread_t;
    pub fn pthread_getattr_np(native: libc::pthread_t,
                              attr: *mut libc::pthread_attr_t) -> libc::c_int;
    pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t,
                                     guardsize: *mut libc::size_t) -> libc::c_int;
    pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t,
                                 stackaddr: *mut *mut libc::c_void,
                                 stacksize: *mut libc::size_t) -> libc::c_int;
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
extern {
    pub fn pthread_self() -> libc::pthread_t;
    fn pthread_set_name_np(tid: libc::pthread_t, name: *const libc::c_char);
}

#[cfg(target_os = "macos")]
extern {
    pub fn pthread_self() -> libc::pthread_t;
    pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void;
    pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t;
    fn pthread_setname_np(name: *const libc::c_char) -> libc::c_int;
}

extern {
    fn pthread_create(native: *mut libc::pthread_t,
                      attr: *const libc::pthread_attr_t,
                      f: StartFn,
                      value: *mut libc::c_void) -> libc::c_int;
    fn pthread_join(native: libc::pthread_t,
                    value: *mut *mut libc::c_void) -> libc::c_int;
    fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
    pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
    fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
                                 stack_size: libc::size_t) -> libc::c_int;
    fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
                                   state: libc::c_int) -> libc::c_int;
    fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
    fn sched_yield() -> libc::c_int;
}
<commit_msg>Thread native name setting, fix #10302<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.

use core::prelude::*;

use boxed::Box;
use cmp;
use mem;
use ptr;
use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN};
use libc;
use thunk::Thunk;
use ffi::CString;

use sys_common::stack::RED_ZONE;
use sys_common::thread::*;

pub type rust_thread = libc::pthread_t;
pub type rust_thread_return = *mut u8;
pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return;

#[no_stack_check]
pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return {
    return start_thread(main);
}

#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))]
pub mod guard {
    pub unsafe fn current() -> uint {
        0
    }

    pub unsafe fn main() -> uint {
        0
    }

    pub unsafe fn init() {
    }
}

#[cfg(any(target_os = "linux", target_os = "macos"))]
pub mod guard {
    use super::*;
    #[cfg(any(target_os = "linux", target_os = "android"))]
    use mem;
    #[cfg(any(target_os = "linux", target_os = "android"))]
    use ptr;
    use libc;
    use libc::funcs::posix88::mman::{mmap};
    use libc::consts::os::posix88::{PROT_NONE,
                                    MAP_PRIVATE,
                                    MAP_ANON,
                                    MAP_FAILED,
                                    MAP_FIXED};

    // These are initialized in init() and only read from after
    static mut PAGE_SIZE: uint = 0;
    static mut GUARD_PAGE: uint = 0;

    #[cfg(target_os = "macos")]
    unsafe fn get_stack_start() -> *mut libc::c_void {
        current() as *mut libc::c_void
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    unsafe fn get_stack_start() -> *mut libc::c_void {
        let mut attr: libc::pthread_attr_t = mem::zeroed();
        if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
            panic!("failed to get thread attributes");
        }
        let mut stackaddr = ptr::null_mut();
        let mut stacksize = 0;
        if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
            panic!("failed to get stack information");
        }
        if pthread_attr_destroy(&mut attr) != 0 {
            panic!("failed to destroy thread attributes");
        }
        stackaddr
    }

    pub unsafe fn init() {
        let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE);
        if psize == -1 {
            panic!("failed to get page size");
        }

        PAGE_SIZE = psize as uint;

        let mut stackaddr = get_stack_start();

        // Ensure stackaddr is page aligned! A parent process might
        // have reset RLIMIT_STACK to be non-page aligned. The
        // pthread_attr_getstack() reports the usable stack area
        // stackaddr < stackaddr + stacksize, so if stackaddr is not
        // page-aligned, calculate the fix such that stackaddr <
        // new_page_aligned_stackaddr < stackaddr + stacksize
        let remainder = (stackaddr as usize) % (PAGE_SIZE as usize);
        if remainder != 0 {
            stackaddr = ((stackaddr as usize) + (PAGE_SIZE as usize) - remainder)
                as *mut libc::c_void;
        }

        // Rellocate the last page of the stack.
        // This ensures SIGBUS will be raised on
        // stack overflow.
        let result = mmap(stackaddr,
                          PAGE_SIZE as libc::size_t,
                          PROT_NONE,
                          MAP_PRIVATE | MAP_ANON | MAP_FIXED,
                          -1,
                          0);

        if result != stackaddr || result == MAP_FAILED {
            panic!("failed to allocate a guard page");
        }

        let offset = if cfg!(target_os = "linux") {
            2
        } else {
            1
        };

        GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE;
    }

    pub unsafe fn main() -> uint {
        GUARD_PAGE
    }

    #[cfg(target_os = "macos")]
    pub unsafe fn current() -> uint {
        (pthread_get_stackaddr_np(pthread_self()) as libc::size_t -
         pthread_get_stacksize_np(pthread_self())) as uint
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    pub unsafe fn current() -> uint {
        let mut attr: libc::pthread_attr_t = mem::zeroed();
        if pthread_getattr_np(pthread_self(), &mut attr) != 0 {
            panic!("failed to get thread attributes");
        }
        let mut guardsize = 0;
        if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 {
            panic!("failed to get stack guard page");
        }
        if guardsize == 0 {
            panic!("there is no guard page");
        }
        let mut stackaddr = ptr::null_mut();
        let mut stacksize = 0;
        if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 {
            panic!("failed to get stack information");
        }
        if pthread_attr_destroy(&mut attr) != 0 {
            panic!("failed to destroy thread attributes");
        }

        stackaddr as uint + guardsize as uint
    }
}

pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread {
    let mut native: libc::pthread_t = mem::zeroed();
    let mut attr: libc::pthread_attr_t = mem::zeroed();
    assert_eq!(pthread_attr_init(&mut attr), 0);
    assert_eq!(pthread_attr_setdetachstate(&mut attr,
                                           PTHREAD_CREATE_JOINABLE), 0);

    // Reserve room for the red zone, the runtime's stack of last resort.
    let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint);
    match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) {
        0 => {
        },
        libc::EINVAL => {
            // EINVAL means |stack_size| is either too small or not a
            // multiple of the system page size.  Because it's definitely
            // >= PTHREAD_STACK_MIN, it must be an alignment issue.
            // Round up to the nearest page and try again.
            let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint;
            let stack_size = (stack_size + page_size - 1) &
                             (-(page_size as int - 1) as uint - 1);
            assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0);
        },
        errno => {
            // This cannot really happen.
            panic!("pthread_attr_setstacksize() error: {}", errno);
        },
    };

    let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint
    let ret = pthread_create(&mut native, &attr, thread_start, arg);
    assert_eq!(pthread_attr_destroy(&mut attr), 0);

    if ret != 0 {
        // be sure to not leak the closure
        let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg);
        panic!("failed to spawn native thread: {}", ret);
    }
    native
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub unsafe fn set_name(name: &str) {
    // pthread_setname_np() since glibc 2.12
    // availability autodetected via weak linkage
    let cname = CString::from_slice(name.as_bytes());
    type F = unsafe extern "C" fn(libc::pthread_t, *const libc::c_char) -> libc::c_int;
    extern {
        #[linkage = "extern_weak"]
        static pthread_setname_np: *const ();
    }
    if !pthread_setname_np.is_null() {
        unsafe {
            mem::transmute::<*const (), F>(pthread_setname_np)(pthread_self(), cname.as_ptr());
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub unsafe fn set_name(name: &str) {
    // pthread_set_name_np() since almost forever on all BSDs
    let cname = CString::from_slice(name.as_bytes());
    pthread_set_name_np(pthread_self(), cname.as_ptr());
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub unsafe fn set_name(name: &str) {
    // pthread_setname_np() since OS X 10.6 and iOS 3.2
    let cname = CString::from_slice(name.as_bytes());
    pthread_setname_np(cname.as_ptr());
}

pub unsafe fn join(native: rust_thread) {
    assert_eq!(pthread_join(native, ptr::null_mut()), 0);
}

pub unsafe fn detach(native: rust_thread) {
    assert_eq!(pthread_detach(native), 0);
}

pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); }
// glibc >= 2.15 has a __pthread_get_minstack() function that returns
// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local
// storage.  We need that information to avoid blowing up when a small stack
// is created in an application with big thread-local storage requirements.
// See #6233 for rationale and details.
//
// Link weakly to the symbol for compatibility with older versions of glibc.
// Assumes that we've been dynamically linked to libpthread but that is
// currently always the case.  Note that you need to check that the symbol
// is non-null before calling it!
#[cfg(target_os = "linux")]
fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t {
    type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t;
    extern {
        #[linkage = "extern_weak"]
        static __pthread_get_minstack: *const ();
    }
    if __pthread_get_minstack.is_null() {
        PTHREAD_STACK_MIN
    } else {
        unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) }
    }
}

// __pthread_get_minstack() is marked as weak but extern_weak linkage is
// not supported on OS X, hence this kludge...
#[cfg(not(target_os = "linux"))]
fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t {
    PTHREAD_STACK_MIN
}

#[cfg(any(target_os = "linux"))]
extern {
    pub fn pthread_self() -> libc::pthread_t;
    pub fn pthread_getattr_np(native: libc::pthread_t,
                              attr: *mut libc::pthread_attr_t) -> libc::c_int;
    pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t,
                                     guardsize: *mut libc::size_t) -> libc::c_int;
    pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t,
                                 stackaddr: *mut *mut libc::c_void,
                                 stacksize: *mut libc::size_t) -> libc::c_int;
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
extern {
    pub fn pthread_self() -> libc::pthread_t;
    fn pthread_set_name_np(tid: libc::pthread_t, name: *const libc::c_char);
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
extern {
    pub fn pthread_self() -> libc::pthread_t;
    pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void;
    pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t;
    fn pthread_setname_np(name: *const libc::c_char) -> libc::c_int;
}

extern {
    fn pthread_create(native: *mut libc::pthread_t,
                      attr: *const libc::pthread_attr_t,
                      f: StartFn,
                      value: *mut libc::c_void) -> libc::c_int;
    fn pthread_join(native: libc::pthread_t,
                    value: *mut *mut libc::c_void) -> libc::c_int;
    fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int;
    pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int;
    fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t,
                                 stack_size: libc::size_t) -> libc::c_int;
    fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t,
                                   state: libc::c_int) -> libc::c_int;
    fn pthread_detach(thread: libc::pthread_t) -> libc::c_int;
    fn sched_yield() -> libc::c_int;
}
<|endoftext|>


<commit_before>
use std::rc::Rc;
use syn::*;

use ast_converters::*;
use tyhandlers::{TypeContext, TypeHandler, get_ty_handler};
use returnhandlers::{ReturnHandler, get_return_handler};
use utils;

#[derive(Debug, PartialEq)]
pub enum ComMethodInfoError {
    TooFewArguments,
    BadSelfArg,
    BadArg(Box<FnArg>),
    BadReturnType,
}

#[derive(Clone)]
pub struct RustArg {
    pub name: Ident,
    pub ty: Type,
    pub handler: Rc<TypeHandler>,
}

impl PartialEq for RustArg {

    fn eq(&self, other: &RustArg) -> bool
    {
        self.name == other.name
            && self.ty == other.ty
    }
}

impl ::std::fmt::Debug for RustArg {
    fn fmt( &self, f: &mut ::std::fmt::Formatter ) -> ::std::fmt::Result {
        write!( f, "{}: {:?}", self.name, self.ty )
    }
}

impl RustArg {

    pub fn new( name: Ident, ty: Type ) -> RustArg {

        let tyhandler = get_ty_handler( &ty, TypeContext::input() );
        RustArg {
            name,
            ty,
            handler: tyhandler,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct ComArg {
    pub name: Ident,
    pub ty: Type,
    pub handler: Rc<TypeHandler>,
    pub dir : Direction
}

impl ComArg {

    pub fn new( name: Ident, ty: Type, dir: Direction ) -> ComArg {

        let tyhandler = get_ty_handler( &ty, TypeContext::new( dir ) );
        ComArg {
            name,
            ty,
            dir,
            handler: tyhandler,
        }
    }

    pub fn from_rustarg( rustarg: RustArg, dir: Direction ) -> ComArg {

        let tyhandler = get_ty_handler( &rustarg.ty, TypeContext::new( dir ) );
        ComArg {
            name: rustarg.name,
            ty: rustarg.ty,
            dir,
            handler: tyhandler,
        }
    }
}

impl PartialEq for ComArg {

    fn eq(&self, other: &ComArg) -> bool
    {
        self.name == other.name
            && self.ty == other.ty
            && self.dir == other.dir
    }
}

impl ::std::fmt::Debug for ComArg {
    fn fmt( &self, f: &mut ::std::fmt::Formatter ) -> ::std::fmt::Result {
        write!( f, "{}: {:?} {:?}", self.name, self.dir, self.ty )
    }
}

#[derive(Debug)]
pub struct ComMethodInfo {

    pub name: Ident,

    pub is_const: bool,
    pub rust_self_arg: ArgSelfRef,
    pub rust_return_ty: Type,

    pub retval_type: Option<Type>,
    pub return_type: Option<Type>,

    pub returnhandler: Box<ReturnHandler>,
    pub args: Vec<RustArg>,

    pub is_unsafe: bool,
}

impl PartialEq for ComMethodInfo {

    fn eq(&self, other: &ComMethodInfo) -> bool
    {
        self.name == other.name
            && self.is_const == other.is_const
            && self.rust_self_arg == other.rust_self_arg
            && self.rust_return_ty == other.rust_return_ty
            && self.retval_type == other.retval_type
            && self.return_type == other.return_type
            && self.args == other.args
    }
}

impl ComMethodInfo {

    /// Constructs new COM method info from a Rust method signature.
    pub fn new(
        m : &MethodSig
    ) -> Result<ComMethodInfo, ComMethodInfoError>
    {
        Self::new_from_parts( m.ident, &m.decl, m.unsafety.is_some() )
    }

    pub fn new_from_parts(
        n: Ident,
        decl: &FnDecl,
        unsafety: bool,
    ) -> Result<ComMethodInfo, ComMethodInfoError>
    {
        // Process all the function arguments.
        // In Rust this includes the 'self' argument and the actual function
        // arguments. For COM the self is implicit so we'll handle it
        // separately.
        let mut iter = decl.inputs.iter();
        let rust_self_arg = iter.next()
                .ok_or_else( || ComMethodInfoError::TooFewArguments )?;

        let ( is_const, rust_self_arg ) = match *rust_self_arg {
            FnArg::SelfRef( ref self_arg ) => (
                self_arg.mutability.is_none(),
                self_arg.clone()
            ),
            _ => return Err( ComMethodInfoError::BadSelfArg ),
        } ;

        // Process other arguments.
        let args = iter.map( | arg | {
            let ty = arg.get_ty()
                .or_else( |_| Err(
                    ComMethodInfoError::BadArg( Box::new( arg.clone() ) )
                ) )?;
            let ident = arg.get_ident()
                .or_else( |_| Err(
                    ComMethodInfoError::BadArg( Box::new( arg.clone() ) )
                ) )?;

            Ok( RustArg::new( ident, ty ) )
        } ).collect::<Result<_,_>>()?;

        // Get the output.
        let rust_return_ty = match decl.output {
            ReturnType::Default => parse_quote!( () ),
            ReturnType::Type( _, ref ty ) => (**ty).clone(),
        };

        // Resolve the return type and retval type.
        let ( retval_type, return_type ) = if utils::is_unit( &rust_return_ty ) {
            ( None, None )
        } else if let Some( ( retval, ret ) ) = try_parse_result( &rust_return_ty ) {
            ( Some( retval ), Some( ret ) )
        } else {
            ( None, Some( rust_return_ty.clone() ) )
        };

        let returnhandler = get_return_handler( &retval_type, &return_type )
                .or( Err( ComMethodInfoError::BadReturnType ) )?;
        Ok( ComMethodInfo {
            name: n,
            is_const,
            rust_self_arg,
            rust_return_ty,
            retval_type,
            return_type,
            returnhandler,
            args,
            is_unsafe: unsafety,
        } )
    }

    pub fn raw_com_args( &self ) -> Vec<ComArg>
    {
        let in_args = self.args
                .iter()
                .map( |ca| {
                    ComArg::from_rustarg( ca.clone(), Direction::In )
                } );
        let out_args = self.returnhandler.com_out_args();

        in_args.chain( out_args ).collect()
    }
}

fn try_parse_result( ty : &Type ) -> Option<( Type, Type )>
{
    let path = match *ty {
        Type::Path( ref p ) => &p.path,
        _ => return None,
    };

    // Ensure the type name contains 'Result'. We don't really have
    // good ways to ensure it is an actual Result type but at least we can
    // use this to discount things like Option<>, etc.
    let last_segment = path.segments.last()?;
    if ! last_segment.value().ident.to_string().contains( "Result" ) {
        return None;
    }

    // Ensure the Result has angle bracket arguments.
    if let PathArguments::AngleBracketed( ref data )
            = last_segment.value().arguments {

        // The returned types depend on how many arguments the Result has.
        return Some( match data.args.len() {
            1 => ( data.args[ 0 ].get_ty().ok()?, hresult_ty() ),
            2 => ( data.args[ 0 ].get_ty().ok()?, data.args[ 1 ].get_ty().ok()? ),
            _ => return None,
        } )
    }

    // We couldn't find a valid type. Return nothing.
    None
}

fn hresult_ty() -> Type {
    parse_quote!( ::intercom::HRESULT )
}

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

    #[test]
    fn no_args_or_return_value() {

        let info = test_info( "fn foo( &self ) {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!( info.retval_type.is_none(), true );
        assert_eq!( info.return_type.is_none(), true );
    }

    #[test]
    fn basic_return_value() {

        let info = test_info( "fn foo( &self ) -> bool {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!( info.retval_type.is_none(), true );
        assert_eq!(
                info.return_type,
                Some( parse_quote!( bool ) ) );
    }

    #[test]
    fn result_return_value() {

        let info = test_info( "fn foo( &self ) -> Result<String, f32> {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!(
                info.retval_type,
                Some( parse_quote!( String ) ) );
        assert_eq!(
                info.return_type,
                Some( parse_quote!( f32 ) ) );
    }

    #[test]
    fn comresult_return_value() {

        let info = test_info( "fn foo( &self ) -> ComResult<String> {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!(
                info.retval_type,
                Some( parse_quote!( String ) ) );
        assert_eq!(
                info.return_type,
                Some( parse_quote!( ::intercom::HRESULT ) ) );
    }

    #[test]
    fn basic_arguments() {

        let info = test_info( "fn foo( &self, a : u32, b : f32 ) {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.retval_type.is_none(), true );
        assert_eq!( info.return_type.is_none(), true );

        assert_eq!( info.args.len(), 2 );

        assert_eq!( info.args[0].name, Ident::from( "a" ) );
        assert_eq!( info.args[0].ty, parse_quote!( u32 ) );

        assert_eq!( info.args[1].name, Ident::from( "b" ) );
        assert_eq!( info.args[1].ty, parse_quote!( f32 ) );
    }

    fn test_info( code : &str ) -> ComMethodInfo {

        let item = parse_str( code ).unwrap();
        let ( ident, decl, unsafety ) = match item {
            Item::Fn( ref f ) => ( f.ident, f.decl.as_ref(), f.unsafety.is_some() ),
            _ => panic!( "Code isn't function" ),
        };
        ComMethodInfo::new_from_parts( ident, decl, unsafety ).unwrap()
    }
}
<commit_msg>Document method info model<commit_after>
use std::rc::Rc;
use syn::*;

use ast_converters::*;
use tyhandlers::{TypeContext, TypeHandler, get_ty_handler};
use returnhandlers::{ReturnHandler, get_return_handler};
use utils;

#[derive(Debug, PartialEq)]
pub enum ComMethodInfoError {
    TooFewArguments,
    BadSelfArg,
    BadArg(Box<FnArg>),
    BadReturnType,
}

#[derive(Clone)]
pub struct RustArg {

    /// Name of the Rust argument.
    pub name: Ident,

    /// Rust type of the COM argument.
    pub ty: Type,

    /// Type handler.
    pub handler: Rc<TypeHandler>,
}

impl PartialEq for RustArg {

    fn eq(&self, other: &RustArg) -> bool
    {
        self.name == other.name
            && self.ty == other.ty
    }
}

impl ::std::fmt::Debug for RustArg {
    fn fmt( &self, f: &mut ::std::fmt::Formatter ) -> ::std::fmt::Result {
        write!( f, "{}: {:?}", self.name, self.ty )
    }
}

impl RustArg {

    pub fn new( name: Ident, ty: Type ) -> RustArg {

        let tyhandler = get_ty_handler( &ty, TypeContext::input() );
        RustArg {
            name,
            ty,
            handler: tyhandler,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct ComArg {

    /// Name of the argument.
    pub name: Ident,

    /// Rust type of the raw COM argument.
    pub ty: Type,

    /// Type handler.
    pub handler: Rc<TypeHandler>,

    /// Argument direction. COM uses OUT params while Rust uses return values.
    pub dir : Direction
}

impl ComArg {

    pub fn new( name: Ident, ty: Type, dir: Direction ) -> ComArg {

        let tyhandler = get_ty_handler( &ty, TypeContext::new( dir ) );
        ComArg {
            name,
            ty,
            dir,
            handler: tyhandler,
        }
    }

    pub fn from_rustarg( rustarg: RustArg, dir: Direction ) -> ComArg {

        let tyhandler = get_ty_handler( &rustarg.ty, TypeContext::new( dir ) );
        ComArg {
            name: rustarg.name,
            ty: rustarg.ty,
            dir,
            handler: tyhandler,
        }
    }
}

impl PartialEq for ComArg {

    fn eq(&self, other: &ComArg) -> bool
    {
        self.name == other.name
            && self.ty == other.ty
            && self.dir == other.dir
    }
}

impl ::std::fmt::Debug for ComArg {
    fn fmt( &self, f: &mut ::std::fmt::Formatter ) -> ::std::fmt::Result {
        write!( f, "{}: {:?} {:?}", self.name, self.dir, self.ty )
    }
}

#[derive(Debug)]
pub struct ComMethodInfo {

    /// Method name.
    pub name: Ident,

    /// True if the self parameter is not mutable.
    pub is_const: bool,

    /// Rust self argument.
    pub rust_self_arg: ArgSelfRef,

    /// Rust return type.
    pub rust_return_ty: Type,

    /// COM retval out parameter type, such as the value of Result<...>.
    pub retval_type: Option<Type>,

    /// COM return type, such as the error value of Result<...>.
    pub return_type: Option<Type>,

    /// Return value handler.
    pub returnhandler: Box<ReturnHandler>,

    /// Method arguments.
    pub args: Vec<RustArg>,

    /// True if the Rust method is unsafe.
    pub is_unsafe: bool,
}

impl PartialEq for ComMethodInfo {

    fn eq(&self, other: &ComMethodInfo) -> bool
    {
        self.name == other.name
            && self.is_const == other.is_const
            && self.rust_self_arg == other.rust_self_arg
            && self.rust_return_ty == other.rust_return_ty
            && self.retval_type == other.retval_type
            && self.return_type == other.return_type
            && self.args == other.args
    }
}

impl ComMethodInfo {

    /// Constructs new COM method info from a Rust method signature.
    pub fn new(
        m : &MethodSig
    ) -> Result<ComMethodInfo, ComMethodInfoError>
    {
        Self::new_from_parts( m.ident, &m.decl, m.unsafety.is_some() )
    }

    pub fn new_from_parts(
        n: Ident,
        decl: &FnDecl,
        unsafety: bool,
    ) -> Result<ComMethodInfo, ComMethodInfoError>
    {
        // Process all the function arguments.
        // In Rust this includes the 'self' argument and the actual function
        // arguments. For COM the self is implicit so we'll handle it
        // separately.
        let mut iter = decl.inputs.iter();
        let rust_self_arg = iter.next()
                .ok_or_else( || ComMethodInfoError::TooFewArguments )?;

        let ( is_const, rust_self_arg ) = match *rust_self_arg {
            FnArg::SelfRef( ref self_arg ) => (
                self_arg.mutability.is_none(),
                self_arg.clone()
            ),
            _ => return Err( ComMethodInfoError::BadSelfArg ),
        } ;

        // Process other arguments.
        let args = iter.map( | arg | {
            let ty = arg.get_ty()
                .or_else( |_| Err(
                    ComMethodInfoError::BadArg( Box::new( arg.clone() ) )
                ) )?;
            let ident = arg.get_ident()
                .or_else( |_| Err(
                    ComMethodInfoError::BadArg( Box::new( arg.clone() ) )
                ) )?;

            Ok( RustArg::new( ident, ty ) )
        } ).collect::<Result<_,_>>()?;

        // Get the output.
        let rust_return_ty = match decl.output {
            ReturnType::Default => parse_quote!( () ),
            ReturnType::Type( _, ref ty ) => (**ty).clone(),
        };

        // Resolve the return type and retval type.
        let ( retval_type, return_type ) = if utils::is_unit( &rust_return_ty ) {
            ( None, None )
        } else if let Some( ( retval, ret ) ) = try_parse_result( &rust_return_ty ) {
            ( Some( retval ), Some( ret ) )
        } else {
            ( None, Some( rust_return_ty.clone() ) )
        };

        let returnhandler = get_return_handler( &retval_type, &return_type )
                .or( Err( ComMethodInfoError::BadReturnType ) )?;
        Ok( ComMethodInfo {
            name: n,
            is_const,
            rust_self_arg,
            rust_return_ty,
            retval_type,
            return_type,
            returnhandler,
            args,
            is_unsafe: unsafety,
        } )
    }

    pub fn raw_com_args( &self ) -> Vec<ComArg>
    {
        let in_args = self.args
                .iter()
                .map( |ca| {
                    ComArg::from_rustarg( ca.clone(), Direction::In )
                } );
        let out_args = self.returnhandler.com_out_args();

        in_args.chain( out_args ).collect()
    }
}

fn try_parse_result( ty : &Type ) -> Option<( Type, Type )>
{
    let path = match *ty {
        Type::Path( ref p ) => &p.path,
        _ => return None,
    };

    // Ensure the type name contains 'Result'. We don't really have
    // good ways to ensure it is an actual Result type but at least we can
    // use this to discount things like Option<>, etc.
    let last_segment = path.segments.last()?;
    if ! last_segment.value().ident.to_string().contains( "Result" ) {
        return None;
    }

    // Ensure the Result has angle bracket arguments.
    if let PathArguments::AngleBracketed( ref data )
            = last_segment.value().arguments {

        // The returned types depend on how many arguments the Result has.
        return Some( match data.args.len() {
            1 => ( data.args[ 0 ].get_ty().ok()?, hresult_ty() ),
            2 => ( data.args[ 0 ].get_ty().ok()?, data.args[ 1 ].get_ty().ok()? ),
            _ => return None,
        } )
    }

    // We couldn't find a valid type. Return nothing.
    None
}

fn hresult_ty() -> Type {
    parse_quote!( ::intercom::HRESULT )
}

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

    #[test]
    fn no_args_or_return_value() {

        let info = test_info( "fn foo( &self ) {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!( info.retval_type.is_none(), true );
        assert_eq!( info.return_type.is_none(), true );
    }

    #[test]
    fn basic_return_value() {

        let info = test_info( "fn foo( &self ) -> bool {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!( info.retval_type.is_none(), true );
        assert_eq!(
                info.return_type,
                Some( parse_quote!( bool ) ) );
    }

    #[test]
    fn result_return_value() {

        let info = test_info( "fn foo( &self ) -> Result<String, f32> {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!(
                info.retval_type,
                Some( parse_quote!( String ) ) );
        assert_eq!(
                info.return_type,
                Some( parse_quote!( f32 ) ) );
    }

    #[test]
    fn comresult_return_value() {

        let info = test_info( "fn foo( &self ) -> ComResult<String> {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.args.len(), 0 );
        assert_eq!(
                info.retval_type,
                Some( parse_quote!( String ) ) );
        assert_eq!(
                info.return_type,
                Some( parse_quote!( ::intercom::HRESULT ) ) );
    }

    #[test]
    fn basic_arguments() {

        let info = test_info( "fn foo( &self, a : u32, b : f32 ) {}" );

        assert_eq!( info.is_const, true );
        assert_eq!( info.name, "foo" );
        assert_eq!( info.retval_type.is_none(), true );
        assert_eq!( info.return_type.is_none(), true );

        assert_eq!( info.args.len(), 2 );

        assert_eq!( info.args[0].name, Ident::from( "a" ) );
        assert_eq!( info.args[0].ty, parse_quote!( u32 ) );

        assert_eq!( info.args[1].name, Ident::from( "b" ) );
        assert_eq!( info.args[1].ty, parse_quote!( f32 ) );
    }

    fn test_info( code : &str ) -> ComMethodInfo {

        let item = parse_str( code ).unwrap();
        let ( ident, decl, unsafety ) = match item {
            Item::Fn( ref f ) => ( f.ident, f.decl.as_ref(), f.unsafety.is_some() ),
            _ => panic!( "Code isn't function" ),
        };
        ComMethodInfo::new_from_parts( ident, decl, unsafety ).unwrap()
    }
}
<|endoftext|>


<commit_before>//! A Rust interface for Objective-C blocks.
//!
//! For more information on the specifics of the block implementation, see
//! Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::{Class, Object};
use {EncodePtr, Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &mut Block<Self, R>) -> R;

    /// Returns an invoke function for a `ConcreteBlock` that takes this type
    /// of arguments.
    fn invoke_for_concrete_block<R, F>() ->
            unsafe extern fn(*mut ConcreteBlock<Self, R, F>, ...) -> R
            where F: Fn<Self, R>;
}

macro_rules! block_args_impl(
    ($f:ident) => (
        block_args_impl!($f,);
    );
    ($f:ident, $($a:ident : $t:ident),*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &mut Block<Self, R>) -> R {
                let invoke: unsafe extern fn(*mut Block<Self, R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                unsafe {
                    invoke(block $(, $a)*)
                }
            }

            fn invoke_for_concrete_block<R, X>() ->
                    unsafe extern fn(*mut ConcreteBlock<Self, R, X>, ...) -> R
                    where X: Fn<Self, R> {
                unsafe extern fn $f<R, X $(, $t)*>(
                        block_ptr: *mut ConcreteBlock<Self, R, X>
                        $(, $a: $t)*) -> R
                        where X: Fn<Self, R> {
                    let block = &*block_ptr;
                    (block.closure)($($a),*)
                }

                unsafe {
                    mem::transmute($f::<R, X $(, $t)*>)
                }
            }
        }
    );
);

block_args_impl!(concrete_block_invoke_args0);
block_args_impl!(concrete_block_invoke_args1, a: A);
block_args_impl!(concrete_block_invoke_args2, a: A, b: B);
block_args_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
block_args_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
block_args_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
block_args_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A, R> where A: BlockArguments {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: unsafe extern fn(*mut Block<A, R>, ...) -> R,
}

// TODO: impl FnMut when it's possible
impl<A: BlockArguments, R> Block<A, R> where A: BlockArguments {
    /// Call self with the given arguments.
    pub fn call(&mut self, args: A) -> R {
        args.call_block(self)
    }
}

unsafe impl<A, R> Message for Block<A, R> where A: BlockArguments { }

impl<A, R> EncodePtr for Block<A, R> {
    fn ptr_code() -> &'static str { "@?" }
}

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A, R, F> where A: BlockArguments {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, F>>>,
    closure: F,
}

impl<A, R, F> ConcreteBlock<A, R, F> where A: BlockArguments, F: Fn<A, R> {
    /// Constructs a `ConcreteBlock` with the given closure.
    /// When the block is called, it will return the value that results from
    /// calling the closure.
    pub fn new(closure: F) -> Self {
        let extern_invoke =
            BlockArguments::invoke_for_concrete_block::<R, F>();
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: unsafe { mem::transmute(extern_invoke) },
            },
            descriptor: Box::new(BlockDescriptor::<Self>::new()),
            closure: closure,
        }
    }

    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            // The copy method is declared as returning an object pointer.
            let block: *mut Object = msg_send![&self.base, copy];
            let block = block as *mut Block<A, R>;
            // At this point, our copy helper has been run so the block will
            // be moved to the heap and we can forget the original block
            // because the heap block will drop in our dispose helper.
            mem::forget(self);
            Id::from_retained_ptr(block)
        }
    }
}

impl<A, R, F> Clone for ConcreteBlock<A, R, F>
        where A: BlockArguments, F: Fn<A, R> + Clone {
    fn clone(&self) -> Self {
        ConcreteBlock::new(self.closure.clone())
    }
}

impl<A, R, F> Deref for ConcreteBlock<A, R, F>
        where A: BlockArguments, F: Fn<A, R> {
    type Target = Block<A, R>;

    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

impl<A, R, F> DerefMut for ConcreteBlock<A, R, F>
        where A: BlockArguments, F: Fn<A, R> {
    fn deref_mut(&mut self) -> &mut Block<A, R> {
        &mut self.base
    }
}

unsafe extern fn block_context_dispose<B>(block: &mut B) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<B>(_dst: &mut B, _src: &B) {
    // The runtime memmoves the src block into the dst block, nothing to do
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<B> BlockDescriptor<B> {
    fn new() -> BlockDescriptor<B> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<B>() as c_ulong,
            copy_helper: block_context_copy::<B>,
            dispose_helper: block_context_dispose::<B>,
        }
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block_with(i: i32) -> Id<Block<(), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block_with(i: i32) -> Id<Block<(i32,), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &mut Block<(), i32>) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *mut _)
        }
    }

    fn invoke_add_block(block: &mut Block<(i32,), i32>, a: i32) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *mut _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let mut block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let mut block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        let mut block = ConcreteBlock::new(|&:| 13);
        let result = invoke_int_block(&mut *block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        let mut block = ConcreteBlock::new(|&: a: i32| a + 5);
        let result = invoke_add_block(&mut *block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_copy() {
        let s = String::from_str("Hello!");
        let expected_len = s.len() as i32;
        let mut block = ConcreteBlock::new(move |&:| s.len() as i32);
        assert!(invoke_int_block(&mut *block) == expected_len);

        let mut copied = block.copy();
        assert!(invoke_int_block(&mut *copied) == expected_len);
    }
}
<commit_msg>Updated Fn trait for latest rust.<commit_after>//! A Rust interface for Objective-C blocks.
//!
//! For more information on the specifics of the block implementation, see
//! Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

use std::mem;
use std::ops::{Deref, DerefMut};
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::{Class, Object};
use {EncodePtr, Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &mut Block<Self, R>) -> R;

    /// Returns an invoke function for a `ConcreteBlock` that takes this type
    /// of arguments.
    fn invoke_for_concrete_block<R, F>() ->
            unsafe extern fn(*mut ConcreteBlock<Self, R, F>, ...) -> R
            where F: Fn<Self, Output=R>;
}

macro_rules! block_args_impl(
    ($f:ident) => (
        block_args_impl!($f,);
    );
    ($f:ident, $($a:ident : $t:ident),*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &mut Block<Self, R>) -> R {
                let invoke: unsafe extern fn(*mut Block<Self, R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                unsafe {
                    invoke(block $(, $a)*)
                }
            }

            fn invoke_for_concrete_block<R, X>() ->
                    unsafe extern fn(*mut ConcreteBlock<Self, R, X>, ...) -> R
                    where X: Fn<Self, Output=R> {
                unsafe extern fn $f<R, X $(, $t)*>(
                        block_ptr: *mut ConcreteBlock<Self, R, X>
                        $(, $a: $t)*) -> R
                        where X: Fn<Self, Output=R> {
                    let block = &*block_ptr;
                    (block.closure)($($a),*)
                }

                unsafe {
                    mem::transmute($f::<R, X $(, $t)*>)
                }
            }
        }
    );
);

block_args_impl!(concrete_block_invoke_args0);
block_args_impl!(concrete_block_invoke_args1, a: A);
block_args_impl!(concrete_block_invoke_args2, a: A, b: B);
block_args_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
block_args_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
block_args_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
block_args_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A, R> where A: BlockArguments {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: unsafe extern fn(*mut Block<A, R>, ...) -> R,
}

// TODO: impl FnMut when it's possible
impl<A: BlockArguments, R> Block<A, R> where A: BlockArguments {
    /// Call self with the given arguments.
    pub fn call(&mut self, args: A) -> R {
        args.call_block(self)
    }
}

unsafe impl<A, R> Message for Block<A, R> where A: BlockArguments { }

impl<A, R> EncodePtr for Block<A, R> {
    fn ptr_code() -> &'static str { "@?" }
}

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A, R, F> where A: BlockArguments {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, F>>>,
    closure: F,
}

impl<A, R, F> ConcreteBlock<A, R, F>
        where A: BlockArguments, F: Fn<A, Output=R> {
    /// Constructs a `ConcreteBlock` with the given closure.
    /// When the block is called, it will return the value that results from
    /// calling the closure.
    pub fn new(closure: F) -> Self {
        let extern_invoke =
            BlockArguments::invoke_for_concrete_block::<R, F>();
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: unsafe { mem::transmute(extern_invoke) },
            },
            descriptor: Box::new(BlockDescriptor::<Self>::new()),
            closure: closure,
        }
    }

    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            // The copy method is declared as returning an object pointer.
            let block: *mut Object = msg_send![&self.base, copy];
            let block = block as *mut Block<A, R>;
            // At this point, our copy helper has been run so the block will
            // be moved to the heap and we can forget the original block
            // because the heap block will drop in our dispose helper.
            mem::forget(self);
            Id::from_retained_ptr(block)
        }
    }
}

impl<A, R, F> Clone for ConcreteBlock<A, R, F>
        where A: BlockArguments, F: Fn<A, Output=R> + Clone {
    fn clone(&self) -> Self {
        ConcreteBlock::new(self.closure.clone())
    }
}

impl<A, R, F> Deref for ConcreteBlock<A, R, F>
        where A: BlockArguments, F: Fn<A, Output=R> {
    type Target = Block<A, R>;

    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

impl<A, R, F> DerefMut for ConcreteBlock<A, R, F>
        where A: BlockArguments, F: Fn<A, Output=R> {
    fn deref_mut(&mut self) -> &mut Block<A, R> {
        &mut self.base
    }
}

unsafe extern fn block_context_dispose<B>(block: &mut B) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<B>(_dst: &mut B, _src: &B) {
    // The runtime memmoves the src block into the dst block, nothing to do
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<B> BlockDescriptor<B> {
    fn new() -> BlockDescriptor<B> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<B>() as c_ulong,
            copy_helper: block_context_copy::<B>,
            dispose_helper: block_context_dispose::<B>,
        }
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block_with(i: i32) -> Id<Block<(), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block_with(i: i32) -> Id<Block<(i32,), i32>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &mut Block<(), i32>) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *mut _)
        }
    }

    fn invoke_add_block(block: &mut Block<(i32,), i32>, a: i32) -> i32 {
        let ptr = block as *mut _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *mut _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let mut block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let mut block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        let mut block = ConcreteBlock::new(|&:| 13);
        let result = invoke_int_block(&mut *block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        let mut block = ConcreteBlock::new(|&: a: i32| a + 5);
        let result = invoke_add_block(&mut *block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_copy() {
        let s = String::from_str("Hello!");
        let expected_len = s.len() as i32;
        let mut block = ConcreteBlock::new(move |&:| s.len() as i32);
        assert!(invoke_int_block(&mut *block) == expected_len);

        let mut copied = block.copy();
        assert!(invoke_int_block(&mut *copied) == expected_len);
    }
}
<|endoftext|>


<commit_before><commit_msg>formatting<commit_after><|endoftext|>


<commit_before>//! Log all requests in a format similar to Heroku's router, but with additional
//! information that we care about like User-Agent and Referer

use conduit::Request;
use std::time::Instant;
use super::prelude::*;
use util::request_header;

#[allow(missing_debug_implementations)] // We can't
#[derive(Default)]
pub struct LogRequests {
    handler: Option<Box<Handler>>,
}

impl AroundMiddleware for LogRequests {
    fn with_handler(&mut self, handler: Box<Handler>) {
        self.handler = Some(handler);
    }
}

impl Handler for LogRequests {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Error + Send>> {
        let request_start = Instant::now();
        let res = self.handler.as_ref().unwrap().call(req);
        let (level, response_code) = match res {
            Ok(ref r) => ("info", r.status.0),
            Err(_) => ("error", 500),
        };
        let response_time = request_start.elapsed();
        let response_time =
            response_time.as_secs() * 1000 + u64::from(response_time.subsec_nanos()) / 1_000_000;

        print!(
            "at={level} method={method} path=\"{path}\" \
             request_id={request_id} fwd=\"{ip}\" service={time_ms}ms \
             status={status} user_agent=\"{user_agent}\" referer=\"{referer}\"",
            level = level,
            method = req.method(),
            path = req.path(),
            ip = request_header(req, "X-Forwarded-For"),
            time_ms = response_time,
            user_agent = request_header(req, "User-Agent"),
            referer = request_header(req, "Referer"), // sic
            request_id = request_header(req, "X-Request-Id"),
            status = response_code,
        );

        if let Err(ref e) = res {
            print!(" error=\"{}\"", e.description());
        }

        println!();

        res
    }
}
<commit_msg>Tag slow requests in our logs<commit_after>//! Log all requests in a format similar to Heroku's router, but with additional
//! information that we care about like User-Agent and Referer

use conduit::Request;
use std::time::Instant;
use super::prelude::*;
use util::request_header;

#[allow(missing_debug_implementations)] // We can't
#[derive(Default)]
pub struct LogRequests {
    handler: Option<Box<Handler>>,
}

impl AroundMiddleware for LogRequests {
    fn with_handler(&mut self, handler: Box<Handler>) {
        self.handler = Some(handler);
    }
}

impl Handler for LogRequests {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Error + Send>> {
        let request_start = Instant::now();
        let res = self.handler.as_ref().unwrap().call(req);
        let (level, response_code) = match res {
            Ok(ref r) => ("info", r.status.0),
            Err(_) => ("error", 500),
        };
        let response_time = request_start.elapsed();
        let response_time =
            response_time.as_secs() * 1000 + u64::from(response_time.subsec_nanos()) / 1_000_000;

        print!(
            "at={level} method={method} path=\"{path}\" \
             request_id={request_id} fwd=\"{ip}\" service={time_ms}ms \
             status={status} user_agent=\"{user_agent}\" referer=\"{referer}\"",
            level = level,
            method = req.method(),
            path = req.path(),
            ip = request_header(req, "X-Forwarded-For"),
            time_ms = response_time,
            user_agent = request_header(req, "User-Agent"),
            referer = request_header(req, "Referer"), // sic
            request_id = request_header(req, "X-Request-Id"),
            status = response_code,
        );

        if let Err(ref e) = res {
            print!(" error=\"{}\"", e.description());
        }

        if response_time > 1000 {
            print!(" SLOW REQUEST");
        }

        println!();

        res
    }
}
<|endoftext|>