<commit_before>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

use frontend::lexer::Token;
use backend::zcode::zfile;
use backend::zcode::zfile::{FormattingState};
use std::collections::HashMap;

//==============================
// ast

pub struct AST {
    passages: Vec<ASTNode>
}

/// add zcode based on tokens
fn gen_zcode<'a>(node: &'a ASTNode, state: FormattingState, mut out: &mut zfile::Zfile,
    mut var_table: &mut HashMap<&'a str, u8>, mut var_id: &mut u8, mut if_count: &mut u32, mut if_stack: &mut Vec<u32>) {

    let mut state_copy = state.clone();

    match node {
        &ASTNode::Passage(ref node) => {
            match &node.category {
                &Token::TokPassageName(ref name) => {
                    out.routine(name, 0);
                },
                _ => {
                    debug!("no match 1");
                }
            };

            for child in &node.childs {
                gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
            }

            out.op_newline();
            out.op_call_1n("system_check_links");

        },
        &ASTNode::Default(ref t) => {
            match &t.category {
                &Token::TokText(ref s) => {
                    out.op_print(s);
                },
                &Token::TokNewLine => {
                    out.op_newline();
                },
                &Token::TokFormatBoldStart => {
                    state_copy.bold = true;
                    out.op_set_text_style(state_copy.bold, state_copy.inverted, state_copy.mono, state_copy.italic);
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
                    }
                    out.op_set_text_style(false, false, false, false);
                    out.op_set_text_style(state.bold, state.inverted, state.mono, state.italic);
                },
                &Token::TokFormatItalicStart => {
                    state_copy.italic = true;
                    out.op_set_text_style(state_copy.bold, state_copy.inverted, state_copy.mono, state_copy.italic);
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
                    }
                    out.op_set_text_style(false, false, false, false);
                    out.op_set_text_style(state.bold, state.inverted, state.mono, state.italic);
                },
                &Token::TokPassageLink (ref name, ref link) => {
                    out.op_call_2n_with_address("system_add_link", link);

                    out.op_set_text_style(state_copy.bold, true, state_copy.mono, state_copy.italic);
                    let link_text = format!("{}[", name);
                    out.op_print(&link_text);
                    out.op_print_num_var(16);
                    out.op_print("]");
                    out.op_set_text_style(state_copy.bold, state_copy.inverted, state_copy.mono, state_copy.italic);
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
                    }
                    out.op_set_text_style(false, false, false, false);
                    out.op_set_text_style(state.bold, state.inverted, state.mono, state.italic);
                },
                &Token::TokAssign(ref var, ref operator) => {
                    if operator == "=" {
                        if !var_table.contains_key::<str>(var) {
                            var_table.insert(&var, *var_id);
                            debug!("Assigned id {} to variable {}", var_id, var);
                            *var_id += 1;
                        }
                        let id_option = var_table.get::<str>(var);
                        if t.childs.len() == 1 {
                            match t.childs[0] {
                                ASTNode::Default(ref def) => {
                                    let actual_id :u8 = match id_option {
                                        Some(id) => {
                                            *id
                                        },
                                        None => {
                                            panic!("Variable not in var table.")
                                        }
                                    };
                                    match def.category {
                                        Token::TokInt(value) => {
                                            out.op_store_u8(actual_id, value as u8);
                                        },
                                        Token::TokBoolean(ref bool_val) => {
                                            let value = match (*bool_val).as_ref() {
                                                "true" => { 1 as u8 },
                                                _ => { 0 as u8 }
                                            };
                                            out.op_store_u8(actual_id, value);
                                        }
                                        _ => { }
                                    }
                                },
                                _ => { }
                            }
                        } else {
                            debug!("Assign Expression currently not supported.");
                        }

                    }
                },
                &Token::TokIf => {
                    if t.childs.len() < 2 {
                        panic!("Unsupported if-expression!");
                    }

                    let mut pseudo_node_pos = 1;
                    let mut compare : u8 = 1;
                    // Check if first token is variable
                    let var_name = match t.childs[0] {
                        ASTNode::Default(ref def) => {
                            match def.category {
                                Token::TokVariable(ref var) => {
                                    var
                                },
                                _ =>  panic!("Unsupported if-expression!")
                            }
                        }, _ => panic!("Unsupported if-expression!")
                    };
                    if t.childs.len() > 2 {
                        pseudo_node_pos = 3;
                        // Check if second token is compare operator
                        match t.childs[1] {
                            ASTNode::Default(ref def) => {
                                match def.category {
                                    Token::TokCompOp(ref op) => {
                                        match &*(*op) {
                                            "==" | "is" => {} ,
                                            _ => panic!("Unsupported Compare Operator!")
                                        }
                                    }, _ =>  panic!("Unsupported if-expression!")
                                }
                            }, _ => panic!("Unsupported if-expression!")
                        }
                        // Check if third token is number
                        compare = match t.childs[2] {
                            ASTNode::Default(ref def) => {
                                match def.category {
                                    Token::TokInt(ref value) => {
                                        *value as u8
                                    },
                                    Token::TokBoolean(ref bool_val) => {
                                        match &*(*bool_val) {
                                            "true" => 1 as u8,
                                            _ => 0 as u8
                                        }
                                    }, _ => panic!("Unsupported assign value!")
                                }
                            }, _ => panic!("Unsupported if-expression!")
                        };
                    }
                    let actual_id :u8 = match var_table.get::<str>(&*(*var_name)) {
                                    Some(id) => {
                                        *id
                                    },
                                    None => {
                                        panic!("Variable not in var table.")
                                    }
                                };

                    let if_label = &format!("if_{}", if_count);
                    let after_if_label = &format!("after_if_{}", if_count);
                    let after_else_label = &format!("after_else_{}", if_count);
                    debug!("{}",if_label);
                    if_stack.push(*if_count);
                    *if_count += 1;
                    out.label(after_else_label);
                    out.op_je(actual_id, compare, if_label);
                    out.op_jump(after_if_label);
                    out.label(if_label);
                    match t.childs[pseudo_node_pos] {
                        ASTNode::Default(ref def) => {
                                match def.category {
                                    Token::TokPseudo => {
                                        for child in &def.childs {
                                            gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack)
                                        }
                                    }, _ => panic!("Unexpected Token!")
                                }
                        }, _ => panic!("Unsupported if-expression!")
                    }
                    out.op_jump(after_else_label);
                    out.label(after_if_label);
                },
                &Token::TokElse => {
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack)
                    }
                },
                &Token::TokEndIf => {
                    debug!("endif");
                    let after_else_label = &format!("after_else_{}", if_stack.pop().unwrap());
                    debug!("{}",after_else_label);
                    out.label(after_else_label);
                },
                _ => {
                    debug!("no match 2");
                }
            };

        }
    };
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(&self,  out: &mut zfile::Zfile) {
        let mut var_table = HashMap::<&str, u8>::new();
        let mut var_id : u8 = 25;
        let mut if_count : u32 = 0;
        let mut if_stack : Vec<u32> = Vec::new();
        let state = FormattingState {bold: false, italic: false, mono: false, inverted: false};
        for child in &self.passages {
            gen_zcode(child, state, out, &mut var_table, &mut var_id, &mut if_count, &mut if_stack);
        }
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].count_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].count_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].count_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }
}
<commit_msg>Finish implementation of basic branching (Closes #53)<commit_after>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

use frontend::lexer::Token;
use backend::zcode::zfile;
use backend::zcode::zfile::{FormattingState};
use std::collections::HashMap;

//==============================
// ast

pub struct AST {
    passages: Vec<ASTNode>
}

/// add zcode based on tokens
fn gen_zcode<'a>(node: &'a ASTNode, state: FormattingState, mut out: &mut zfile::Zfile,
    mut var_table: &mut HashMap<&'a str, u8>, mut var_id: &mut u8, mut if_count: &mut u32, mut if_stack: &mut Vec<u32>) {

    let mut state_copy = state.clone();

    match node {
        &ASTNode::Passage(ref node) => {
            match &node.category {
                &Token::TokPassageName(ref name) => {
                    out.routine(name, 0);
                },
                _ => {
                    debug!("no match 1");
                }
            };

            for child in &node.childs {
                gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
            }

            out.op_newline();
            out.op_call_1n("system_check_links");

        },
        &ASTNode::Default(ref t) => {
            match &t.category {
                &Token::TokText(ref s) => {
                    out.op_print(s);
                },
                &Token::TokNewLine => {
                    out.op_newline();
                },
                &Token::TokFormatBoldStart => {
                    state_copy.bold = true;
                    out.op_set_text_style(state_copy.bold, state_copy.inverted, state_copy.mono, state_copy.italic);
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
                    }
                    out.op_set_text_style(false, false, false, false);
                    out.op_set_text_style(state.bold, state.inverted, state.mono, state.italic);
                },
                &Token::TokFormatItalicStart => {
                    state_copy.italic = true;
                    out.op_set_text_style(state_copy.bold, state_copy.inverted, state_copy.mono, state_copy.italic);
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
                    }
                    out.op_set_text_style(false, false, false, false);
                    out.op_set_text_style(state.bold, state.inverted, state.mono, state.italic);
                },
                &Token::TokPassageLink (ref name, ref link) => {
                    out.op_call_2n_with_address("system_add_link", link);

                    out.op_set_text_style(state_copy.bold, true, state_copy.mono, state_copy.italic);
                    let link_text = format!("{}[", name);
                    out.op_print(&link_text);
                    out.op_print_num_var(16);
                    out.op_print("]");
                    out.op_set_text_style(state_copy.bold, state_copy.inverted, state_copy.mono, state_copy.italic);
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack);
                    }
                    out.op_set_text_style(false, false, false, false);
                    out.op_set_text_style(state.bold, state.inverted, state.mono, state.italic);
                },
                &Token::TokAssign(ref var, ref operator) => {
                    if operator == "=" {
                        if !var_table.contains_key::<str>(var) {
                            var_table.insert(&var, *var_id);
                            debug!("Assigned id {} to variable {}", var_id, var);
                            *var_id += 1;
                        }
                        let id_option = var_table.get::<str>(var);
                        if t.childs.len() == 1 {
                            match t.childs[0] {
                                ASTNode::Default(ref def) => {
                                    let actual_id :u8 = match id_option {
                                        Some(id) => {
                                            *id
                                        },
                                        None => {
                                            panic!("Variable not in var table.")
                                        }
                                    };
                                    match def.category {
                                        Token::TokInt(value) => {
                                            out.op_store_u8(actual_id, value as u8);
                                        },
                                        Token::TokBoolean(ref bool_val) => {
                                            let value = match (*bool_val).as_ref() {
                                                "true" => { 1 as u8 },
                                                _ => { 0 as u8 }
                                            };
                                            out.op_store_u8(actual_id, value);
                                        }
                                        _ => { }
                                    }
                                },
                                _ => { }
                            }
                        } else {
                            debug!("Assign Expression currently not supported.");
                        }

                    }
                },
                &Token::TokIf => {
                    if t.childs.len() < 2 {
                        panic!("Unsupported if-expression!");
                    }

                    let mut compare : u8 = 1;

                    // check if the first node is a pseudonode
                    let pseudo_node = match t.childs[0] {
                        ASTNode::Default(ref def) => {
                            match def.category {
                                Token::TokPseudo => {
                                    def
                                },
                                _ =>  panic!("Unsupported if-expression!")
                            }
                        }, _ => panic!("Unsupported if-expression!")
                    };

                    // Check if first token is variable
                    let var_name = match pseudo_node.childs[0] {
                        ASTNode::Default(ref def) => {
                            match def.category {
                                Token::TokVariable(ref var) => {
                                    var
                                },
                                _ =>  panic!("Unsupported if-expression!")
                            }
                        }, _ => panic!("Unsupported if-expression!")
                    };
                    if pseudo_node.childs.len() > 1 {
                        // Check if second token is compare operator
                        match pseudo_node.childs[1] {
                            ASTNode::Default(ref def) => {
                                match def.category {
                                    Token::TokCompOp(ref op) => {
                                        match &*(*op) {
                                            "==" | "is" => {} ,
                                            _ => panic!("Unsupported Compare Operator!")
                                        }
                                    }, _ =>  panic!("Unsupported if-expression!")
                                }
                            }, _ => panic!("Unsupported if-expression!")
                        }
                        // Check if third token is number
                        compare = match pseudo_node.childs[2] {
                            ASTNode::Default(ref def) => {
                                match def.category {
                                    Token::TokInt(ref value) => {
                                        *value as u8
                                    },
                                    Token::TokBoolean(ref bool_val) => {
                                        match &*(*bool_val) {
                                            "true" => 1 as u8,
                                            _ => 0 as u8
                                        }
                                    }, _ => panic!("Unsupported assign value!")
                                }
                            }, _ => panic!("Unsupported if-expression!")
                        };
                    }
                    let actual_id :u8 = match var_table.get::<str>(&*(*var_name)) {
                        Some(id) => *id,
                        None => panic!("Variable not in var table.")
                    };

                    let if_label = &format!("if_{}", if_count);
                    let after_if_label = &format!("after_if_{}", if_count);
                    let after_else_label = &format!("after_else_{}", if_count);
                    if_stack.push(*if_count);
                    *if_count += 1;
                    out.op_je(actual_id, compare, if_label);
                    out.op_jump(after_if_label);
                    out.label(if_label);

                    for i in 1..t.childs.len() {
                        gen_zcode(&t.childs[i], state_copy, out, var_table, var_id, if_count, if_stack)
                    }

                    out.op_jump(after_else_label);
                    out.label(after_if_label);
                },
                &Token::TokElse => {
                    for child in &t.childs {
                        gen_zcode(child, state_copy, out, var_table, var_id, if_count, if_stack)
                    }
                },
                &Token::TokEndIf => {
                    let after_else_label = &format!("after_else_{}", if_stack.pop().unwrap());
                    out.label(after_else_label);
                },
                _ => {
                    debug!("no match 2");
                }
            };
        }
    };
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(&self,  out: &mut zfile::Zfile) {
        let mut var_table = HashMap::<&str, u8>::new();
        let mut var_id : u8 = 25;
        let mut if_count : u32 = 0;
        let mut if_stack : Vec<u32> = Vec::new();
        let state = FormattingState {bold: false, italic: false, mono: false, inverted: false};
        for child in &self.passages {
            gen_zcode(child, state, out, &mut var_table, &mut var_id, &mut if_count, &mut if_stack);
        }
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].count_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].count_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].count_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::time::Duration;

use calloop::timer::Timer;
use smithay::{
    backend::{
        renderer::{gles2::Gles2Renderer, Frame, Renderer},
        winit::{self, WinitEvent, WinitEventLoop, WinitGraphicsBackend},
    },
    desktop::space::SurfaceTree,
    reexports::{calloop::EventLoop, wayland_server::protocol::wl_output},
    utils::{Rectangle, Transform},
    wayland::output::{Mode, Output, PhysicalProperties},
};

use slog::Logger;

use crate::CalloopData;

smithay::custom_elements! {
    pub CustomElem<=Gles2Renderer>;
    SurfaceTree=SurfaceTree,
}

pub fn run_winit(
    event_loop: &mut EventLoop<CalloopData>,
    data: &mut CalloopData,
    log: Logger,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    let (mut backend, mut winit) = winit::init(log.clone())?;

    let mode = Mode {
        size: backend.window_size().physical_size,
        refresh: 60_000,
    };

    let (output, _global) = Output::new(
        display,
        "winit".to_string(),
        PhysicalProperties {
            size: (0, 0).into(),
            subpixel: wl_output::Subpixel::Unknown,
            make: "Smithay".into(),
            model: "Winit".into(),
        },
        log.clone(),
    );
    output.change_current_state(
        &mut display.handle(),
        Some(mode),
        Some(wl_output::Transform::Flipped180),
        None,
        Some((0, 0).into()),
    );
    output.set_preferred(mode);

    state.space.map_output(&output, 1.0, (0, 0));

    std::env::set_var("WAYLAND_DISPLAY", "wayland-5");

    let mut full_redraw = 0u8;

    let timer = Timer::<()>::new().unwrap();

    timer.handle().add_timeout(Duration::ZERO, ());
    event_loop.handle().insert_source(timer, move |_, timer, data| {
        // run_test(&mut backend, &mut winit, data, &output);
        winit_dispatch(&mut backend, &mut winit, data, &output, &mut full_redraw).unwrap();
        timer.add_timeout(Duration::from_millis(16), ());
    })?;

    Ok(())
}

pub fn winit_dispatch(
    backend: &mut WinitGraphicsBackend,
    winit: &mut WinitEventLoop,
    data: &mut CalloopData,
    output: &Output,
    full_redraw: &mut u8,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    winit.dispatch_new_events(|event| match event {
        WinitEvent::Resized { .. } => {}
        WinitEvent::Input(event) => state.process_input_event(display, event),
        _ => (),
    })?;

    *full_redraw = full_redraw.saturating_sub(1);

    let size = backend.window_size().physical_size;
    let damage = Rectangle::from_loc_and_size((0, 0), size);

    backend.bind().ok().and_then(|_| {
        state
            .space
            .render_output::<Gles2Renderer, CustomElem>(
                &mut display.handle(),
                backend.renderer(),
                &output,
                0,
                [0.1, 0.1, 0.1, 1.0],
                &[],
            )
            .unwrap()
    });

    backend.submit(Some(&[damage.to_logical(1)]), 1.0).unwrap();

    state.space.send_frames(
        &mut display.handle(),
        state.start_time.elapsed().as_millis() as u32,
    );

    state.space.refresh(&mut display.handle());
    display.flush_clients()?;

    Ok(())
}
<commit_msg>smallvil: Handle winit window resizes<commit_after>use std::time::Duration;

use calloop::timer::Timer;
use smithay::{
    backend::{
        renderer::{gles2::Gles2Renderer, Frame, Renderer},
        winit::{self, WinitEvent, WinitEventLoop, WinitGraphicsBackend},
    },
    desktop::space::SurfaceTree,
    reexports::{calloop::EventLoop, wayland_server::protocol::wl_output},
    utils::{Rectangle, Transform},
    wayland::output::{Mode, Output, PhysicalProperties},
};

use slog::Logger;

use crate::CalloopData;

smithay::custom_elements! {
    pub CustomElem<=Gles2Renderer>;
    SurfaceTree=SurfaceTree,
}

pub fn run_winit(
    event_loop: &mut EventLoop<CalloopData>,
    data: &mut CalloopData,
    log: Logger,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    let (mut backend, mut winit) = winit::init(log.clone())?;

    let mode = Mode {
        size: backend.window_size().physical_size,
        refresh: 60_000,
    };

    let (output, _global) = Output::new(
        display,
        "winit".to_string(),
        PhysicalProperties {
            size: (0, 0).into(),
            subpixel: wl_output::Subpixel::Unknown,
            make: "Smithay".into(),
            model: "Winit".into(),
        },
        log.clone(),
    );
    output.change_current_state(
        &mut display.handle(),
        Some(mode),
        Some(wl_output::Transform::Flipped180),
        None,
        Some((0, 0).into()),
    );
    output.set_preferred(mode);

    state.space.map_output(&output, 1.0, (0, 0));

    std::env::set_var("WAYLAND_DISPLAY", "wayland-5");

    let mut full_redraw = 0u8;

    let timer = Timer::<()>::new().unwrap();

    timer.handle().add_timeout(Duration::ZERO, ());
    event_loop.handle().insert_source(timer, move |_, timer, data| {
        // run_test(&mut backend, &mut winit, data, &output);
        winit_dispatch(&mut backend, &mut winit, data, &output, &mut full_redraw).unwrap();
        timer.add_timeout(Duration::from_millis(16), ());
    })?;

    Ok(())
}

pub fn winit_dispatch(
    backend: &mut WinitGraphicsBackend,
    winit: &mut WinitEventLoop,
    data: &mut CalloopData,
    output: &Output,
    full_redraw: &mut u8,
) -> Result<(), Box<dyn std::error::Error>> {
    let display = &mut data.display;
    let state = &mut data.state;

    winit.dispatch_new_events(|event| match event {
        WinitEvent::Resized { size, .. } => {
            output.change_current_state(
                &mut display.handle(),
                Some(Mode {
                    size,
                    refresh: 60_000,
                }),
                None,
                None,
                None,
            );
        }
        WinitEvent::Input(event) => state.process_input_event(display, event),
        _ => (),
    })?;

    *full_redraw = full_redraw.saturating_sub(1);

    let size = backend.window_size().physical_size;
    let damage = Rectangle::from_loc_and_size((0, 0), size);

    backend.bind().ok().and_then(|_| {
        state
            .space
            .render_output::<Gles2Renderer, CustomElem>(
                &mut display.handle(),
                backend.renderer(),
                &output,
                0,
                [0.1, 0.1, 0.1, 1.0],
                &[],
            )
            .unwrap()
    });

    backend.submit(Some(&[damage.to_logical(1)]), 1.0).unwrap();

    state.space.send_frames(
        &mut display.handle(),
        state.start_time.elapsed().as_millis() as u32,
    );

    state.space.refresh(&mut display.handle());
    display.flush_clients()?;

    Ok(())
}
<|endoftext|>


<commit_before>//! Lazily compute the reverse control-flow graph for the MIR.

use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::OnceCell;
use rustc_index::vec::IndexVec;
use rustc_serialize as serialize;
use smallvec::SmallVec;

use crate::mir::{BasicBlock, BasicBlockData};

// Typically 95%+ of basic blocks have 4 or fewer predecessors.
pub type Predecessors = IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>;

#[derive(Clone, Debug)]
pub(super) struct PredecessorCache {
    cache: OnceCell<Predecessors>,
}

impl PredecessorCache {
    #[inline]
    pub(super) fn new() -> Self {
        PredecessorCache { cache: OnceCell::new() }
    }

    /// Invalidates the predecessor cache.
    ///
    /// Invalidating the predecessor cache requires mutating the MIR, which in turn requires a
    /// unique reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all
    /// callers of `invalidate` have a unique reference to the MIR and thus to the predecessor
    /// cache. This means we never need to do synchronization when `invalidate` is called.
    #[inline]
    pub(super) fn invalidate(&mut self) {
        self.cache = OnceCell::new();
    }

    /// Returns the the predecessor graph for this MIR.
    #[inline]
    pub(super) fn compute(
        &self,
        basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
    ) -> &Predecessors {
        self.cache.get_or_init(|| {
            let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks);
            for (bb, data) in basic_blocks.iter_enumerated() {
                if let Some(term) = &data.terminator {
                    for &succ in term.successors() {
                        preds[succ].push(bb);
                    }
                }
            }

            preds
        })
    }
}

impl serialize::Encodable for PredecessorCache {
    #[inline]
    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        serialize::Encodable::encode(&(), s)
    }
}

impl serialize::Decodable for PredecessorCache {
    #[inline]
    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
        serialize::Decodable::decode(d).map(|_v: ()| Self::new())
    }
}

impl<CTX> HashStable<CTX> for PredecessorCache {
    #[inline]
    fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
        // do nothing
    }
}

CloneTypeFoldableAndLiftImpls! {
    PredecessorCache,
}
<commit_msg>Switch to non-doc comment<commit_after>//! Lazily compute the reverse control-flow graph for the MIR.

use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::OnceCell;
use rustc_index::vec::IndexVec;
use rustc_serialize as serialize;
use smallvec::SmallVec;

use crate::mir::{BasicBlock, BasicBlockData};

// Typically 95%+ of basic blocks have 4 or fewer predecessors.
pub type Predecessors = IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>;

#[derive(Clone, Debug)]
pub(super) struct PredecessorCache {
    cache: OnceCell<Predecessors>,
}

impl PredecessorCache {
    #[inline]
    pub(super) fn new() -> Self {
        PredecessorCache { cache: OnceCell::new() }
    }

    /// Invalidates the predecessor cache.
    #[inline]
    pub(super) fn invalidate(&mut self) {
        // Invalidating the predecessor cache requires mutating the MIR, which in turn requires a
        // unique reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all
        // callers of `invalidate` have a unique reference to the MIR and thus to the predecessor
        // cache. This means we never need to do synchronization when `invalidate` is called, we can
        // simply reinitialize the `OnceCell`.
        self.cache = OnceCell::new();
    }

    /// Returns the the predecessor graph for this MIR.
    #[inline]
    pub(super) fn compute(
        &self,
        basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
    ) -> &Predecessors {
        self.cache.get_or_init(|| {
            let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks);
            for (bb, data) in basic_blocks.iter_enumerated() {
                if let Some(term) = &data.terminator {
                    for &succ in term.successors() {
                        preds[succ].push(bb);
                    }
                }
            }

            preds
        })
    }
}

impl serialize::Encodable for PredecessorCache {
    #[inline]
    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        serialize::Encodable::encode(&(), s)
    }
}

impl serialize::Decodable for PredecessorCache {
    #[inline]
    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
        serialize::Decodable::decode(d).map(|_v: ()| Self::new())
    }
}

impl<CTX> HashStable<CTX> for PredecessorCache {
    #[inline]
    fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
        // do nothing
    }
}

CloneTypeFoldableAndLiftImpls! {
    PredecessorCache,
}
<|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.

//! Method lookup: the secret sauce of Rust. See the [rustc guide] chapter.
//!
//! [rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/method-lookup.html

use check::FnCtxt;
use hir::def::Def;
use hir::def_id::DefId;
use namespace::Namespace;
use rustc::ty::subst::Substs;
use rustc::traits;
use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
use rustc::ty::GenericParamDefKind;
use rustc::ty::subst::Subst;
use rustc::infer::{self, InferOk};

use syntax::ast;
use syntax_pos::Span;

use rustc::hir;

use rustc_data_structures::sync::Lrc;

pub use self::MethodError::*;
pub use self::CandidateSource::*;
pub use self::suggest::TraitInfo;

mod confirm;
pub mod probe;
mod suggest;

use self::probe::{IsSuggestion, ProbeScope};

pub fn provide(providers: &mut ty::query::Providers) {
    suggest::provide(providers);
}

#[derive(Clone, Copy, Debug)]
pub struct MethodCallee<'tcx> {
    /// Impl method ID, for inherent methods, or trait method ID, otherwise.
    pub def_id: DefId,
    pub substs: &'tcx Substs<'tcx>,

    /// Instantiated method signature, i.e. it has been
    /// substituted, normalized, and has had late-bound
    /// lifetimes replaced with inference variables.
    pub sig: ty::FnSig<'tcx>,
}

pub enum MethodError<'tcx> {
    // Did not find an applicable method, but we did find various near-misses that may work.
    NoMatch(NoMatchData<'tcx>),

    // Multiple methods might apply.
    Ambiguity(Vec<CandidateSource>),

    // Found an applicable method, but it is not visible. The second argument contains a list of
    // not-in-scope traits which may work.
    PrivateMatch(Def, Vec<DefId>),

    // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
    // forgotten to import a trait.
    IllegalSizedBound(Vec<DefId>),

    // Found a match, but the return type is wrong
    BadReturnType,
}

// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
pub struct NoMatchData<'tcx> {
    pub static_candidates: Vec<CandidateSource>,
    pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
    pub out_of_scope_traits: Vec<DefId>,
    pub lev_candidate: Option<ty::AssociatedItem>,
    pub mode: probe::Mode,
}

impl<'tcx> NoMatchData<'tcx> {
    pub fn new(static_candidates: Vec<CandidateSource>,
               unsatisfied_predicates: Vec<TraitRef<'tcx>>,
               out_of_scope_traits: Vec<DefId>,
               lev_candidate: Option<ty::AssociatedItem>,
               mode: probe::Mode)
               -> Self {
        NoMatchData {
            static_candidates,
            unsatisfied_predicates,
            out_of_scope_traits,
            lev_candidate,
            mode,
        }
    }
}

// A pared down enum describing just the places from which a method
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CandidateSource {
    ImplSource(DefId),
    TraitSource(// trait id
                DefId),
}

impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
    /// Determines whether the type `self_ty` supports a method name `method_name` or not.
    pub fn method_exists(&self,
                         method_name: ast::Ident,
                         self_ty: Ty<'tcx>,
                         call_expr_id: ast::NodeId,
                         allow_private: bool)
                         -> bool {
        let mode = probe::Mode::MethodCall;
        match self.probe_for_name(method_name.span, mode, method_name,
                                  IsSuggestion(false), self_ty, call_expr_id,
                                  ProbeScope::TraitsInScope) {
            Ok(..) => true,
            Err(NoMatch(..)) => false,
            Err(Ambiguity(..)) => true,
            Err(PrivateMatch(..)) => allow_private,
            Err(IllegalSizedBound(..)) => true,
            Err(BadReturnType) => {
                bug!("no return type expectations but got BadReturnType")
            }

        }
    }

    /// Performs method lookup. If lookup is successful, it will return the callee
    /// and store an appropriate adjustment for the self-expr. In some cases it may
    /// report an error (e.g., invoking the `drop` method).
    ///
    /// # Arguments
    ///
    /// Given a method call like `foo.bar::<T1,...Tn>(...)`:
    ///
    /// * `fcx`:                   the surrounding `FnCtxt` (!)
    /// * `span`:                  the span for the method call
    /// * `method_name`:           the name of the method being called (`bar`)
    /// * `self_ty`:               the (unadjusted) type of the self expression (`foo`)
    /// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
    /// * `self_expr`:             the self expression (`foo`)
    pub fn lookup_method(&self,
                         self_ty: Ty<'tcx>,
                         segment: &hir::PathSegment,
                         span: Span,
                         call_expr: &'gcx hir::Expr,
                         self_expr: &'gcx hir::Expr)
                         -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
        debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
               segment.ident,
               self_ty,
               call_expr,
               self_expr);

        let pick = self.lookup_probe(
            span,
            segment.ident,
            self_ty,
            call_expr,
            ProbeScope::TraitsInScope
        )?;

        if let Some(import_id) = pick.import_id {
            let import_def_id = self.tcx.hir.local_def_id(import_id);
            debug!("used_trait_import: {:?}", import_def_id);
            Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
                                        .unwrap().insert(import_def_id);
        }

        self.tcx.check_stability(pick.item.def_id, Some(call_expr.id), span);

        let result = self.confirm_method(
            span,
            self_expr,
            call_expr,
            self_ty,
            pick.clone(),
            segment,
        );

        if result.illegal_sized_bound {
            // We probe again, taking all traits into account (not only those in scope).
            let candidates =
                match self.lookup_probe(span,
                                        segment.ident,
                                        self_ty,
                                        call_expr,
                                        ProbeScope::AllTraits) {

                    // If we find a different result the caller probably forgot to import a trait.
                    Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
                    Err(Ambiguity(ref sources)) => {
                        sources.iter()
                               .filter_map(|source| {
                                   match *source {
                                       // Note: this cannot come from an inherent impl,
                                       // because the first probing succeeded.
                                       ImplSource(def) => self.tcx.trait_id_of_impl(def),
                                       TraitSource(_) => None,
                                   }
                               })
                               .collect()
                    }
                    _ => Vec::new(),
                };

            return Err(IllegalSizedBound(candidates));
        }

        Ok(result.callee)
    }

    fn lookup_probe(&self,
                    span: Span,
                    method_name: ast::Ident,
                    self_ty: Ty<'tcx>,
                    call_expr: &'gcx hir::Expr,
                    scope: ProbeScope)
                    -> probe::PickResult<'tcx> {
        let mode = probe::Mode::MethodCall;
        let self_ty = self.resolve_type_vars_if_possible(&self_ty);
        self.probe_for_name(span, mode, method_name, IsSuggestion(false),
                            self_ty, call_expr.id, scope)
    }

    /// `lookup_method_in_trait` is used for overloaded operators.
    /// It does a very narrow slice of what the normal probe/confirm path does.
    /// In particular, it doesn't really do any probing: it simply constructs
    /// an obligation for a particular trait with the given self-type and checks
    /// whether that trait is implemented.
    ///
    /// FIXME(#18741) -- It seems likely that we can consolidate some of this
    /// code with the other method-lookup code. In particular, the second half
    /// of this method is basically the same as confirmation.
    pub fn lookup_method_in_trait(&self,
                                  span: Span,
                                  m_name: ast::Ident,
                                  trait_def_id: DefId,
                                  self_ty: Ty<'tcx>,
                                  opt_input_types: Option<&[Ty<'tcx>]>)
                                  -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
        debug!("lookup_in_trait_adjusted(self_ty={:?}, \
                m_name={}, trait_def_id={:?})",
               self_ty,
               m_name,
               trait_def_id);

        // Construct a trait-reference `self_ty : Trait<input_tys>`
        let substs = Substs::for_item(self.tcx, trait_def_id, |param, _| {
            match param.kind {
                GenericParamDefKind::Lifetime => {}
                GenericParamDefKind::Type {..} => {
                    if param.index == 0 {
                        return self_ty.into();
                    } else if let Some(ref input_types) = opt_input_types {
                        return input_types[param.index as usize - 1].into();
                    }
                }
            }
            self.var_for_def(span, param)
        });

        let trait_ref = ty::TraitRef::new(trait_def_id, substs);

        // Construct an obligation
        let poly_trait_ref = trait_ref.to_poly_trait_ref();
        let obligation =
            traits::Obligation::misc(span,
                                     self.body_id,
                                     self.param_env,
                                     poly_trait_ref.to_predicate());

        // Now we want to know if this can be matched
        if !self.predicate_may_hold(&obligation) {
            debug!("--> Cannot match obligation");
            return None; // Cannot be matched, no such method resolution is possible.
        }

        // Trait must have a method named `m_name` and it should not have
        // type parameters or early-bound regions.
        let tcx = self.tcx;
        let method_item =
            self.associated_item(trait_def_id, m_name, Namespace::Value)?;
        let def_id = method_item.def_id;
        let generics = tcx.generics_of(def_id);
        assert_eq!(generics.params.len(), 0);

        debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
        let mut obligations = vec![];

        // Instantiate late-bound regions and substitute the trait
        // parameters into the method type to get the actual method type.
        //
        // NB: Instantiate late-bound regions first so that
        // `instantiate_type_scheme` can normalize associated types that
        // may reference those regions.
        let fn_sig = tcx.fn_sig(def_id);
        let fn_sig = self.replace_late_bound_regions_with_fresh_var(span,
                                                                    infer::FnCall,
                                                                    &fn_sig).0;
        let fn_sig = fn_sig.subst(self.tcx, substs);
        let fn_sig = match self.normalize_associated_types_in_as_infer_ok(span, &fn_sig) {
            InferOk { value, obligations: o } => {
                obligations.extend(o);
                value
            }
        };

        // Register obligations for the parameters.  This will include the
        // `Self` parameter, which in turn has a bound of the main trait,
        // so this also effectively registers `obligation` as well.  (We
        // used to register `obligation` explicitly, but that resulted in
        // double error messages being reported.)
        //
        // Note that as the method comes from a trait, it should not have
        // any late-bound regions appearing in its bounds.
        let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
        let bounds = match self.normalize_associated_types_in_as_infer_ok(span, &bounds) {
            InferOk { value, obligations: o } => {
                obligations.extend(o);
                value
            }
        };
        assert!(!bounds.has_escaping_bound_vars());

        let cause = traits::ObligationCause::misc(span, self.body_id);
        obligations.extend(traits::predicates_for_generics(cause.clone(),
                                                           self.param_env,
                                                           &bounds));

        // Also add an obligation for the method type being well-formed.
        let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig));
        debug!("lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
               method_ty,
               obligation);
        obligations.push(traits::Obligation::new(cause,
                                                 self.param_env,
                                                 ty::Predicate::WellFormed(method_ty)));

        let callee = MethodCallee {
            def_id,
            substs: trait_ref.substs,
            sig: fn_sig,
        };

        debug!("callee = {:?}", callee);

        Some(InferOk {
            obligations,
            value: callee
        })
    }

    pub fn resolve_ufcs(&self,
                        span: Span,
                        method_name: ast::Ident,
                        self_ty: Ty<'tcx>,
                        expr_id: ast::NodeId)
                        -> Result<Def, MethodError<'tcx>> {
        let mode = probe::Mode::Path;
        let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
                                       self_ty, expr_id, ProbeScope::TraitsInScope)?;

        if let Some(import_id) = pick.import_id {
            let import_def_id = self.tcx.hir.local_def_id(import_id);
            debug!("used_trait_import: {:?}", import_def_id);
            Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
                                        .unwrap().insert(import_def_id);
        }

        let def = pick.item.def();
        self.tcx.check_stability(def.def_id(), Some(expr_id), span);

        Ok(def)
    }

    /// Find item with name `item_name` defined in impl/trait `def_id`
    /// and return it, or `None`, if no such item was defined there.
    pub fn associated_item(&self, def_id: DefId, item_name: ast::Ident, ns: Namespace)
                           -> Option<ty::AssociatedItem> {
        self.tcx.associated_items(def_id).find(|item| {
            Namespace::from(item.kind) == ns &&
            self.tcx.hygienic_eq(item_name, item.ident, def_id)
        })
    }
}
<commit_msg>add call to tcx.sess.delay_span_bug<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.

//! Method lookup: the secret sauce of Rust. See the [rustc guide] chapter.
//!
//! [rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/method-lookup.html

use check::FnCtxt;
use hir::def::Def;
use hir::def_id::DefId;
use namespace::Namespace;
use rustc::ty::subst::Substs;
use rustc::traits;
use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
use rustc::ty::GenericParamDefKind;
use rustc::ty::subst::Subst;
use rustc::infer::{self, InferOk};

use syntax::ast;
use syntax_pos::Span;

use rustc::hir;

use rustc_data_structures::sync::Lrc;

pub use self::MethodError::*;
pub use self::CandidateSource::*;
pub use self::suggest::TraitInfo;

mod confirm;
pub mod probe;
mod suggest;

use self::probe::{IsSuggestion, ProbeScope};

pub fn provide(providers: &mut ty::query::Providers) {
    suggest::provide(providers);
}

#[derive(Clone, Copy, Debug)]
pub struct MethodCallee<'tcx> {
    /// Impl method ID, for inherent methods, or trait method ID, otherwise.
    pub def_id: DefId,
    pub substs: &'tcx Substs<'tcx>,

    /// Instantiated method signature, i.e. it has been
    /// substituted, normalized, and has had late-bound
    /// lifetimes replaced with inference variables.
    pub sig: ty::FnSig<'tcx>,
}

pub enum MethodError<'tcx> {
    // Did not find an applicable method, but we did find various near-misses that may work.
    NoMatch(NoMatchData<'tcx>),

    // Multiple methods might apply.
    Ambiguity(Vec<CandidateSource>),

    // Found an applicable method, but it is not visible. The second argument contains a list of
    // not-in-scope traits which may work.
    PrivateMatch(Def, Vec<DefId>),

    // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
    // forgotten to import a trait.
    IllegalSizedBound(Vec<DefId>),

    // Found a match, but the return type is wrong
    BadReturnType,
}

// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
pub struct NoMatchData<'tcx> {
    pub static_candidates: Vec<CandidateSource>,
    pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
    pub out_of_scope_traits: Vec<DefId>,
    pub lev_candidate: Option<ty::AssociatedItem>,
    pub mode: probe::Mode,
}

impl<'tcx> NoMatchData<'tcx> {
    pub fn new(static_candidates: Vec<CandidateSource>,
               unsatisfied_predicates: Vec<TraitRef<'tcx>>,
               out_of_scope_traits: Vec<DefId>,
               lev_candidate: Option<ty::AssociatedItem>,
               mode: probe::Mode)
               -> Self {
        NoMatchData {
            static_candidates,
            unsatisfied_predicates,
            out_of_scope_traits,
            lev_candidate,
            mode,
        }
    }
}

// A pared down enum describing just the places from which a method
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CandidateSource {
    ImplSource(DefId),
    TraitSource(// trait id
                DefId),
}

impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
    /// Determines whether the type `self_ty` supports a method name `method_name` or not.
    pub fn method_exists(&self,
                         method_name: ast::Ident,
                         self_ty: Ty<'tcx>,
                         call_expr_id: ast::NodeId,
                         allow_private: bool)
                         -> bool {
        let mode = probe::Mode::MethodCall;
        match self.probe_for_name(method_name.span, mode, method_name,
                                  IsSuggestion(false), self_ty, call_expr_id,
                                  ProbeScope::TraitsInScope) {
            Ok(..) => true,
            Err(NoMatch(..)) => false,
            Err(Ambiguity(..)) => true,
            Err(PrivateMatch(..)) => allow_private,
            Err(IllegalSizedBound(..)) => true,
            Err(BadReturnType) => {
                bug!("no return type expectations but got BadReturnType")
            }

        }
    }

    /// Performs method lookup. If lookup is successful, it will return the callee
    /// and store an appropriate adjustment for the self-expr. In some cases it may
    /// report an error (e.g., invoking the `drop` method).
    ///
    /// # Arguments
    ///
    /// Given a method call like `foo.bar::<T1,...Tn>(...)`:
    ///
    /// * `fcx`:                   the surrounding `FnCtxt` (!)
    /// * `span`:                  the span for the method call
    /// * `method_name`:           the name of the method being called (`bar`)
    /// * `self_ty`:               the (unadjusted) type of the self expression (`foo`)
    /// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
    /// * `self_expr`:             the self expression (`foo`)
    pub fn lookup_method(&self,
                         self_ty: Ty<'tcx>,
                         segment: &hir::PathSegment,
                         span: Span,
                         call_expr: &'gcx hir::Expr,
                         self_expr: &'gcx hir::Expr)
                         -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
        debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
               segment.ident,
               self_ty,
               call_expr,
               self_expr);

        let pick = self.lookup_probe(
            span,
            segment.ident,
            self_ty,
            call_expr,
            ProbeScope::TraitsInScope
        )?;

        if let Some(import_id) = pick.import_id {
            let import_def_id = self.tcx.hir.local_def_id(import_id);
            debug!("used_trait_import: {:?}", import_def_id);
            Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
                                        .unwrap().insert(import_def_id);
        }

        self.tcx.check_stability(pick.item.def_id, Some(call_expr.id), span);

        let result = self.confirm_method(
            span,
            self_expr,
            call_expr,
            self_ty,
            pick.clone(),
            segment,
        );

        if result.illegal_sized_bound {
            // We probe again, taking all traits into account (not only those in scope).
            let candidates =
                match self.lookup_probe(span,
                                        segment.ident,
                                        self_ty,
                                        call_expr,
                                        ProbeScope::AllTraits) {

                    // If we find a different result the caller probably forgot to import a trait.
                    Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
                    Err(Ambiguity(ref sources)) => {
                        sources.iter()
                               .filter_map(|source| {
                                   match *source {
                                       // Note: this cannot come from an inherent impl,
                                       // because the first probing succeeded.
                                       ImplSource(def) => self.tcx.trait_id_of_impl(def),
                                       TraitSource(_) => None,
                                   }
                               })
                               .collect()
                    }
                    _ => Vec::new(),
                };

            return Err(IllegalSizedBound(candidates));
        }

        Ok(result.callee)
    }

    fn lookup_probe(&self,
                    span: Span,
                    method_name: ast::Ident,
                    self_ty: Ty<'tcx>,
                    call_expr: &'gcx hir::Expr,
                    scope: ProbeScope)
                    -> probe::PickResult<'tcx> {
        let mode = probe::Mode::MethodCall;
        let self_ty = self.resolve_type_vars_if_possible(&self_ty);
        self.probe_for_name(span, mode, method_name, IsSuggestion(false),
                            self_ty, call_expr.id, scope)
    }

    /// `lookup_method_in_trait` is used for overloaded operators.
    /// It does a very narrow slice of what the normal probe/confirm path does.
    /// In particular, it doesn't really do any probing: it simply constructs
    /// an obligation for a particular trait with the given self-type and checks
    /// whether that trait is implemented.
    ///
    /// FIXME(#18741) -- It seems likely that we can consolidate some of this
    /// code with the other method-lookup code. In particular, the second half
    /// of this method is basically the same as confirmation.
    pub fn lookup_method_in_trait(&self,
                                  span: Span,
                                  m_name: ast::Ident,
                                  trait_def_id: DefId,
                                  self_ty: Ty<'tcx>,
                                  opt_input_types: Option<&[Ty<'tcx>]>)
                                  -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
        debug!("lookup_in_trait_adjusted(self_ty={:?}, \
                m_name={}, trait_def_id={:?})",
               self_ty,
               m_name,
               trait_def_id);

        // Construct a trait-reference `self_ty : Trait<input_tys>`
        let substs = Substs::for_item(self.tcx, trait_def_id, |param, _| {
            match param.kind {
                GenericParamDefKind::Lifetime => {}
                GenericParamDefKind::Type {..} => {
                    if param.index == 0 {
                        return self_ty.into();
                    } else if let Some(ref input_types) = opt_input_types {
                        return input_types[param.index as usize - 1].into();
                    }
                }
            }
            self.var_for_def(span, param)
        });

        let trait_ref = ty::TraitRef::new(trait_def_id, substs);

        // Construct an obligation
        let poly_trait_ref = trait_ref.to_poly_trait_ref();
        let obligation =
            traits::Obligation::misc(span,
                                     self.body_id,
                                     self.param_env,
                                     poly_trait_ref.to_predicate());

        // Now we want to know if this can be matched
        if !self.predicate_may_hold(&obligation) {
            debug!("--> Cannot match obligation");
            return None; // Cannot be matched, no such method resolution is possible.
        }

        // Trait must have a method named `m_name` and it should not have
        // type parameters or early-bound regions.
        let tcx = self.tcx;
        let method_item = match self.associated_item(trait_def_id, m_name, Namespace::Value) {
            Some(method_item) => method_item,
            None => {
                tcx.sess.delay_span_bug(span,
                    "operator trait does not have corresponding operator method");
                return None;
            }
        };
        let def_id = method_item.def_id;
        let generics = tcx.generics_of(def_id);
        assert_eq!(generics.params.len(), 0);

        debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
        let mut obligations = vec![];

        // Instantiate late-bound regions and substitute the trait
        // parameters into the method type to get the actual method type.
        //
        // NB: Instantiate late-bound regions first so that
        // `instantiate_type_scheme` can normalize associated types that
        // may reference those regions.
        let fn_sig = tcx.fn_sig(def_id);
        let fn_sig = self.replace_late_bound_regions_with_fresh_var(span,
                                                                    infer::FnCall,
                                                                    &fn_sig).0;
        let fn_sig = fn_sig.subst(self.tcx, substs);
        let fn_sig = match self.normalize_associated_types_in_as_infer_ok(span, &fn_sig) {
            InferOk { value, obligations: o } => {
                obligations.extend(o);
                value
            }
        };

        // Register obligations for the parameters.  This will include the
        // `Self` parameter, which in turn has a bound of the main trait,
        // so this also effectively registers `obligation` as well.  (We
        // used to register `obligation` explicitly, but that resulted in
        // double error messages being reported.)
        //
        // Note that as the method comes from a trait, it should not have
        // any late-bound regions appearing in its bounds.
        let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs);
        let bounds = match self.normalize_associated_types_in_as_infer_ok(span, &bounds) {
            InferOk { value, obligations: o } => {
                obligations.extend(o);
                value
            }
        };
        assert!(!bounds.has_escaping_bound_vars());

        let cause = traits::ObligationCause::misc(span, self.body_id);
        obligations.extend(traits::predicates_for_generics(cause.clone(),
                                                           self.param_env,
                                                           &bounds));

        // Also add an obligation for the method type being well-formed.
        let method_ty = tcx.mk_fn_ptr(ty::Binder::bind(fn_sig));
        debug!("lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
               method_ty,
               obligation);
        obligations.push(traits::Obligation::new(cause,
                                                 self.param_env,
                                                 ty::Predicate::WellFormed(method_ty)));

        let callee = MethodCallee {
            def_id,
            substs: trait_ref.substs,
            sig: fn_sig,
        };

        debug!("callee = {:?}", callee);

        Some(InferOk {
            obligations,
            value: callee
        })
    }

    pub fn resolve_ufcs(&self,
                        span: Span,
                        method_name: ast::Ident,
                        self_ty: Ty<'tcx>,
                        expr_id: ast::NodeId)
                        -> Result<Def, MethodError<'tcx>> {
        let mode = probe::Mode::Path;
        let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
                                       self_ty, expr_id, ProbeScope::TraitsInScope)?;

        if let Some(import_id) = pick.import_id {
            let import_def_id = self.tcx.hir.local_def_id(import_id);
            debug!("used_trait_import: {:?}", import_def_id);
            Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
                                        .unwrap().insert(import_def_id);
        }

        let def = pick.item.def();
        self.tcx.check_stability(def.def_id(), Some(expr_id), span);

        Ok(def)
    }

    /// Find item with name `item_name` defined in impl/trait `def_id`
    /// and return it, or `None`, if no such item was defined there.
    pub fn associated_item(&self, def_id: DefId, item_name: ast::Ident, ns: Namespace)
                           -> Option<ty::AssociatedItem> {
        self.tcx.associated_items(def_id).find(|item| {
            Namespace::from(item.kind) == ns &&
            self.tcx.hygienic_eq(item_name, item.ident, def_id)
        })
    }
}
<|endoftext|>


<commit_before>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines. Any line that starts with a # is ignored.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u32 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.
const BROADCAST_PORT_UDP : u16 = 27900;
const SERVER_LIST_PORT_TCP : u16 = 29920;

// Broadcast packet types.
const KEEPALIVE : u8 = 8;
const HEARTBEAT : u8 = 3;

// Game state changes
const GAMEEXITED : u16 = 2;

// Opcode info
const OPCODE_INDEX : usize = 0;
const OPCODE_AND_HANDSHAKE_LENGTH : usize = 5;

// Broadcasted game name
const HALO_RETAIL : &'static str = "halor";

use std::net::{UdpSocket,TcpListener,SocketAddr};
use std::net::SocketAddr::{V4,V6};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::sync::{Arc, Mutex};

extern crate time;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::HeartbeatPacket;

trait IPString {
    fn ip_string(&self) -> String;
}

impl IPString for SocketAddr {
    fn ip_string(&self) -> String {
        match *self {
            V4(ipv4) => ipv4.ip().to_string(),
            V6(ipv6) => "[".to_string() + &ipv6.ip().to_string() + "]"
        }
    }
}

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let j : Vec<_> = env::args().collect();
        j[1].to_string()
    }
    else if count == 1 {
        "0.0.0.0".to_string()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = match UdpSocket::bind((&ip as &str,BROADCAST_PORT_UDP)) {
        Err(error) => {
            println!("Error creating a UDP socket at {}:{}. {}.",ip,BROADCAST_PORT_UDP,error);
            return;
        },
        Ok(halo_socket) => halo_socket
    };

    let client_socket = match TcpListener::bind((&ip as &str,SERVER_LIST_PORT_TCP)) {
        Err(error) => {
            println!("Error listening to TCP at {}:{}. {}.",ip,SERVER_LIST_PORT_TCP,error);
            return;
        },
        Ok(client_socket) => client_socket
    };

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    thread::spawn(move || {
        loop {
            thread::sleep_ms(10 * 1000);
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = time::now().to_timespec().sec;
            servers.retain(|x| x.last_alive + DROP_TIME > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist: Option<Vec<String>> = None;
    let blacklist_update = Arc::new(Mutex::new(blacklist));
    let blacklist_udp = blacklist_update.clone();

    // Blacklist read thread.
    thread::spawn(move || {
        let valid_line = |x: &str| -> bool { x.trim().len() > 0 && !x.starts_with("#") };
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_ref = blacklist_update.lock().unwrap();
                *blacklist_ref =
                    File::open(BLACKLIST_FILE).
                    map(|file|
                        BufReader::new(&file).lines().
                        filter_map(|line| line.ok().and_then(|x| if valid_line(&x) { Some(x) } else { None })).
                        collect()
                    ).ok();
            }
            thread::sleep_ms(BLACKLIST_UPDATE_TIME * 1000);
        }
    });

    // TCP server thread. This is for the HaloMD application.
    thread::spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = match stream {
                    Err(_) => continue,
                    Ok(the_stream) => the_stream
                };
                // Unwrap the IP.
                let ip = match client.peer_addr() {
                    Err(_) => continue,
                    Ok(ip) => ip.ip_string()
                };

                let mut ips = String::new();

                // Make servers_ref go out of scope to unlock it for other threads, since we don't need it.
                {
                    let servers_ref = servers_mut_tcp.lock().unwrap();
                    let servers = (*servers_ref).iter();

                    for j in servers {
                        ips.push_str(&(j.to_string()));
                        ips.push('\n');
                    }
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips.push_str(&ip);
                ips.push_str(":49149:3425");

                // We may be here a while. Just in case...
                thread::spawn( move || {
                    let _ = client.write_all(ips.as_bytes());
                });
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    // These are the allowed game versions. HaloMD and Halo PC 1.09 uses 01.00.09.0620, while Halo PC servers on 1.10 use 01.00.10.0621 (these are also joinable).
    let game_versions = [ "01.00.09.0620".to_string(), "01.00.10.0621".to_string() ];

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = match halo_socket.recv_from(&mut buffer) {
            Err(_) => continue,
            Ok(x) => x
        };
        if length <= OPCODE_INDEX {
            continue;
        }

        if buffer[OPCODE_INDEX] != KEEPALIVE && buffer[OPCODE_INDEX] != HEARTBEAT {
            continue;
        }

        let client_ip = source.ip_string();

        let blacklist_ref = blacklist_udp.lock().unwrap();
        if (*blacklist_ref).as_ref().map(|blacklist| blacklist.contains(&client_ip)).unwrap_or(false) {
            continue;
        }

        // Heartbeat packet. These contain null-terminated C strings and are ordered in key1[0]value1[0]key2[0]value2[0]key3[0]value3[0] where [0] is a byte equal to 0x00.
        if buffer[OPCODE_INDEX] == HEARTBEAT && length > OPCODE_AND_HANDSHAKE_LENGTH {

            let mut servers = servers_mut_udp.lock().unwrap();

            match HeartbeatPacket::from_buffer(&buffer[OPCODE_AND_HANDSHAKE_LENGTH..length]) {
                None => {},
                Some(packet) => {
                    let updatetime = time::now().to_timespec().sec;
                    match servers.iter_mut().position(|x| x.ip == client_ip && x.port == packet.localport) {
                        None => {
                            if game_versions.contains(&packet.gamever) && &packet.gamename == HALO_RETAIL {
                                let serverness = HaloServer { ip:client_ip, port: packet.localport, last_alive: updatetime };
                                (*servers).push(serverness);
                            }
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == GAMEEXITED {
                                servers.remove(k);
                            }
                        }
                    };
                }
            };
        }

        // Keepalive packet. We need to rely on the origin's port for this, unfortunately. This may mean that the source port is incorrect if the port was changed with NAT.
        else if buffer[OPCODE_INDEX] == KEEPALIVE {
            let mut servers_ref = servers_mut_udp.lock().unwrap();
            let servers = (*servers_ref).iter_mut();

            for i in servers {
                if i.ip == client_ip && i.port == source.port() {
                    i.last_alive = time::now().to_timespec().sec;
                    break;
                }
            }
        }
    }
}
<commit_msg>Define constants for 1.09 and 1.10 and try to use &str<commit_after>// Syntax: master_server [ip address (0.0.0.0 by default)]
// This was created for HaloMD.

// If the server does not respond in at least this many seconds, it will be dropped from the list.
const DROP_TIME : i64 = 60;

// Blacklist for blocking IPs. Separate with newlines. Any line that starts with a # is ignored.
// Blacklisting IPs ignores heartbeat and keepalive packets from an IP address.
// That means that servers that are banned will not be immediately removed, but will time out, instead.
const BLACKLIST_FILE : &'static str = "blacklist.txt";

// Read the blacklist every x amount of seconds.
const BLACKLIST_UPDATE_TIME : u32 = 60;

// Note: The master server must have TCP 29920 open and UDP 27900 open.
const BROADCAST_PORT_UDP : u16 = 27900;
const SERVER_LIST_PORT_TCP : u16 = 29920;

// Broadcast packet types.
const KEEPALIVE : u8 = 8;
const HEARTBEAT : u8 = 3;

// Game state changes
const GAMEEXITED : u16 = 2;

// Opcode info
const OPCODE_INDEX : usize = 0;
const OPCODE_AND_HANDSHAKE_LENGTH : usize = 5;

// Broadcasted game name
const HALO_RETAIL : &'static str = "halor";

// Halo game versions
const HALO_VERSION_1_09 : &'static str = "01.00.09.0620";
const HALO_VERSION_1_10 : &'static str = "01.00.10.0621";

use std::net::{UdpSocket,TcpListener,SocketAddr};
use std::net::SocketAddr::{V4,V6};
use std::io::{Write,BufReader,BufRead};
use std::env;
use std::fs::File;
use std::thread;
use std::sync::{Arc, Mutex};

extern crate time;

mod halo_server;
use halo_server::HaloServer;

mod heartbeat_packet;
use heartbeat_packet::HeartbeatPacket;

trait IPString {
    fn ip_string(&self) -> String;
}

impl IPString for SocketAddr {
    fn ip_string(&self) -> String {
        match *self {
            V4(ipv4) => ipv4.ip().to_string(),
            V6(ipv6) => "[".to_string() + &ipv6.ip().to_string() + "]"
        }
    }
}

fn main() {
    let count = env::args().count();
    let ip = if count == 2 {
        let j : Vec<_> = env::args().collect();
        j[1].to_string()
    }
    else if count == 1 {
        "0.0.0.0".to_string()
    }
    else {
        println!("Only one argument is allowed: the IP to bind to.");
        return;
    };

    // We need to bind on two different ports. If it failed to bind (invalid IP, port is taken), then we must make sure this is known.
    let halo_socket = match UdpSocket::bind((&ip as &str,BROADCAST_PORT_UDP)) {
        Err(error) => {
            println!("Error creating a UDP socket at {}:{}. {}.",ip,BROADCAST_PORT_UDP,error);
            return;
        },
        Ok(halo_socket) => halo_socket
    };

    let client_socket = match TcpListener::bind((&ip as &str,SERVER_LIST_PORT_TCP)) {
        Err(error) => {
            println!("Error listening to TCP at {}:{}. {}.",ip,SERVER_LIST_PORT_TCP,error);
            return;
        },
        Ok(client_socket) => client_socket
    };

    // Mutex for thread safety.
    let servers_halo: Vec<HaloServer> = Vec::new();
    let servers_mut_udp = Arc::new(Mutex::new(servers_halo));
    let servers_mut_tcp = servers_mut_udp.clone();
    let servers_mut_destruction = servers_mut_udp.clone();

    // Destruction thread. This will remove servers that have not broadcasted their presence in a while.
    thread::spawn(move || {
        loop {
            thread::sleep_ms(10 * 1000);
            let mut servers = servers_mut_destruction.lock().unwrap();
            let timenow = time::now().to_timespec().sec;
            servers.retain(|x| x.last_alive + DROP_TIME > timenow);
        }
    });

    // Blacklist mutex. Concurrency needs to be safe, my friend.
    let blacklist: Option<Vec<String>> = None;
    let blacklist_update = Arc::new(Mutex::new(blacklist));
    let blacklist_udp = blacklist_update.clone();

    // Blacklist read thread.
    thread::spawn(move || {
        let valid_line = |x: &str| -> bool { x.trim().len() > 0 && !x.starts_with("#") };
        loop {
            // Placed in a block so blacklist is unlocked before sleeping to prevent threads from being locked for too long.
            {
                let mut blacklist_ref = blacklist_update.lock().unwrap();
                *blacklist_ref =
                    File::open(BLACKLIST_FILE).
                    map(|file|
                        BufReader::new(&file).lines().
                        filter_map(|line| line.ok().and_then(|x| if valid_line(&x) { Some(x) } else { None })).
                        collect()
                    ).ok();
            }
            thread::sleep_ms(BLACKLIST_UPDATE_TIME * 1000);
        }
    });

    // TCP server thread. This is for the HaloMD application.
    thread::spawn(move || {
        loop {
            for stream in client_socket.incoming() {
                let mut client = match stream {
                    Err(_) => continue,
                    Ok(the_stream) => the_stream
                };
                // Unwrap the IP.
                let ip = match client.peer_addr() {
                    Err(_) => continue,
                    Ok(ip) => ip.ip_string()
                };

                let mut ips = String::new();

                // Make servers_ref go out of scope to unlock it for other threads, since we don't need it.
                {
                    let servers_ref = servers_mut_tcp.lock().unwrap();
                    let servers = (*servers_ref).iter();

                    for j in servers {
                        ips.push_str(&(j.to_string()));
                        ips.push('\n');
                    }
                }

                // Some number placed after the requester's IP. If you ask me, the source code was abducted by aliens, and this is a tracking number. Regardless, it's needed.
                ips.push_str(&ip);
                ips.push_str(":49149:3425");

                // We may be here a while. Just in case...
                thread::spawn( move || {
                    let _ = client.write_all(ips.as_bytes());
                });
            }
        }
    });

    // UDP server is run on the main thread. Servers broadcast their presence here.

    // These are the allowed game versions. HaloMD uses 1.09, but Halo PC 1.10 is interoperable
    let game_versions = [ HALO_VERSION_1_09, HALO_VERSION_1_10 ];

    let mut buffer = [0; 2048];
    loop {
        let (length, source) = match halo_socket.recv_from(&mut buffer) {
            Err(_) => continue,
            Ok(x) => x
        };
        if length <= OPCODE_INDEX {
            continue;
        }

        if buffer[OPCODE_INDEX] != KEEPALIVE && buffer[OPCODE_INDEX] != HEARTBEAT {
            continue;
        }

        let client_ip = source.ip_string();

        let blacklist_ref = blacklist_udp.lock().unwrap();
        if (*blacklist_ref).as_ref().map(|blacklist| blacklist.contains(&client_ip)).unwrap_or(false) {
            continue;
        }

        // Heartbeat packet. These contain null-terminated C strings and are ordered in key1[0]value1[0]key2[0]value2[0]key3[0]value3[0] where [0] is a byte equal to 0x00.
        if buffer[OPCODE_INDEX] == HEARTBEAT && length > OPCODE_AND_HANDSHAKE_LENGTH {

            let mut servers = servers_mut_udp.lock().unwrap();

            match HeartbeatPacket::from_buffer(&buffer[OPCODE_AND_HANDSHAKE_LENGTH..length]) {
                None => {},
                Some(packet) => {
                    let updatetime = time::now().to_timespec().sec;
                    match servers.iter_mut().position(|x| x.ip == client_ip && x.port == packet.localport) {
                        None => {
                            if game_versions.contains(&&*packet.gamever) && packet.gamename == HALO_RETAIL {
                                let serverness = HaloServer { ip:client_ip, port: packet.localport, last_alive: updatetime };
                                (*servers).push(serverness);
                            }
                        }
                        Some(k) => {
                            servers[k].last_alive = updatetime;
                            if packet.statechanged == GAMEEXITED {
                                servers.remove(k);
                            }
                        }
                    };
                }
            };
        }

        // Keepalive packet. We need to rely on the origin's port for this, unfortunately. This may mean that the source port is incorrect if the port was changed with NAT.
        else if buffer[OPCODE_INDEX] == KEEPALIVE {
            let mut servers_ref = servers_mut_udp.lock().unwrap();
            let servers = (*servers_ref).iter_mut();

            for i in servers {
                if i.ip == client_ip && i.port == source.port() {
                    i.last_alive = time::now().to_timespec().sec;
                    break;
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate zookeeper;

use std::time::Duration;
use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZkResult, ZooKeeper};
use zookeeper::perms;

struct LoggingWatcher;
impl Watcher for LoggingWatcher {
    fn handle(&self, e: &WatchedEvent) {
        println!("{}", e)
    }
}

fn zk_example() -> ZkResult<()> {
    let zk = try!(ZooKeeper::connect("127.0.0.1:2181", Duration::seconds(5), LoggingWatcher));

    let acl1 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];
    let acl2 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];

    let auth = zk.add_auth("digest", vec![1,2,3,4]);

    println!("authenticated -> {}", auth);

    let path = zk.create("/test", vec![1,2], acl1, CreateMode::Ephemeral);

    println!("created -> {}", path);

    let exists = zk.exists("/test", true);

    println!("exists -> {}", exists);

    let dont_exists = zk.exists("/blabla", true);

    println!("don't exists path -> {}", dont_exists);

    let get_acl = zk.get_acl("/test");

    println!("get_acl -> {}", get_acl);

    let set_acl = zk.set_acl("/test", acl2, -1);

    println!("set_acl -> {}", set_acl);

    let children = zk.get_children("/", true);

    println!("children of / -> {}", children);

    let set_data = zk.set_data("/test", vec![6,5,4,3], -1);

    println!("set_data -> {}", set_data);

    let get_data = zk.get_data("/test", true);

    println!("get_data -> {}", get_data);

    let delete = zk.delete("/test", -1);

    println!("deleted /test -> {}", delete);

    println!("press enter to close client");
    std::io::stdin().read_line();

    // Showing thet this client can be shared between tasks
    let zk2 = zk.clone();
    spawn(proc() {
        zk2.close();
    });

    println!("press enter to exit example");
    std::io::stdin().read_line();

    Ok(())
}

fn main() {
    zk_example().unwrap();
}
<commit_msg>Rephrased comment<commit_after>extern crate zookeeper;

use std::time::Duration;
use zookeeper::{Acl, CreateMode, Watcher, WatchedEvent, ZkResult, ZooKeeper};
use zookeeper::perms;

struct LoggingWatcher;
impl Watcher for LoggingWatcher {
    fn handle(&self, e: &WatchedEvent) {
        println!("{}", e)
    }
}

fn zk_example() -> ZkResult<()> {
    let zk = try!(ZooKeeper::connect("127.0.0.1:2181", Duration::seconds(5), LoggingWatcher));

    let acl1 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];
    let acl2 = vec![Acl{perms: perms::ALL, scheme: "world".to_string(), id: "anyone".to_string()}];

    let auth = zk.add_auth("digest", vec![1,2,3,4]);

    println!("authenticated -> {}", auth);

    let path = zk.create("/test", vec![1,2], acl1, CreateMode::Ephemeral);

    println!("created -> {}", path);

    let exists = zk.exists("/test", true);

    println!("exists -> {}", exists);

    let dont_exists = zk.exists("/blabla", true);

    println!("don't exists path -> {}", dont_exists);

    let get_acl = zk.get_acl("/test");

    println!("get_acl -> {}", get_acl);

    let set_acl = zk.set_acl("/test", acl2, -1);

    println!("set_acl -> {}", set_acl);

    let children = zk.get_children("/", true);

    println!("children of / -> {}", children);

    let set_data = zk.set_data("/test", vec![6,5,4,3], -1);

    println!("set_data -> {}", set_data);

    let get_data = zk.get_data("/test", true);

    println!("get_data -> {}", get_data);

    let delete = zk.delete("/test", -1);

    println!("deleted /test -> {}", delete);

    println!("press enter to close client");
    std::io::stdin().read_line();

    // The client can be shared between tasks
    let zk2 = zk.clone();
    spawn(proc() {
        zk2.close();
    });

    println!("press enter to exit example");
    std::io::stdin().read_line();

    Ok(())
}

fn main() {
    zk_example().unwrap();
}
<|endoftext|>