<commit_before>use std::collections::{HashMap, HashSet};
use intern::{intern, read, InternedString};
use grammar::parse_tree::{Alternative, Condition, ConditionOp, ExprSymbol, Grammar, GrammarItem,
                          MacroSymbol, NonterminalData, RepeatOp, RepeatSymbol,
                          Span, Symbol, TypeRef};
use normalize::{NormResult, NormError};
use normalize::norm_util::{self, Symbols};
use regex::Regex;
use std::mem;

#[cfg(test)]
mod test;

pub fn expand_macros(input: Grammar) -> NormResult<Grammar> {
    let Grammar { span, type_name, items } = input;

    let (macro_defs, mut items): (Vec<_>, Vec<_>) =
        items.into_iter().partition(|mi| mi.is_macro_def());

    let macro_defs: HashMap<InternedString, NonterminalData> =
        macro_defs.into_iter()
                  .map(|md| match md {
                      GrammarItem::Nonterminal(data) => (data.name, data),
                      _ => unreachable!()
                  })
                  .collect();

    let mut expander = MacroExpander::new(macro_defs);
    try!(expander.expand(&mut items));

    Ok(Grammar { span: span, type_name: type_name, items: items })
}

struct MacroExpander {
    macro_defs: HashMap<InternedString, NonterminalData>,
    expansion_set: HashSet<InternedString>,
    expansion_stack: Vec<Symbol>,
}

impl MacroExpander {
    fn new(macro_defs: HashMap<InternedString, NonterminalData>) -> MacroExpander {
        MacroExpander {
            macro_defs: macro_defs,
            expansion_stack: Vec::new(),
            expansion_set: HashSet::new()
        }
    }

    fn expand(&mut self, items: &mut Vec<GrammarItem>) -> NormResult<()> {
        let mut counter = 0;
        loop {
            let old_expansions = self.expansion_set.len();

            println!("Next round: counter={} items.len()={}",
                     counter, items.len());

            // Find any macro uses in items added since last round and
            // replace them in place with the expanded version:
            for item in &mut items[counter..] {
                self.replace_item(item);
            }
            counter = items.len();

            // No more expansion to do.
            if self.expansion_set.len() == old_expansions {
                return Ok(());
            }

            // Drain expansion stack:
            while let Some(sym) = self.expansion_stack.pop() {
                println!("Drained {:?}", sym);
                match sym {
                    Symbol::Macro(msym) =>
                        items.push(try!(self.expand_macro_symbol(msym))),
                    Symbol::Expr(expr) =>
                        items.push(try!(self.expand_expr_symbol(expr))),
                    Symbol::Repeat(repeat) =>
                        items.push(try!(self.expand_repeat_symbol(*repeat))),
                    _ =>
                        assert!(false, "don't know how to expand `{:?}`", sym)
                }
            }
        }
    }

    fn replace_item(&mut self, item: &mut GrammarItem) {
        match *item {
            GrammarItem::TokenType(..) => { }
            GrammarItem::Nonterminal(ref mut data) => {
                // Should not encounter macro definitions here,
                // they've already been siphoned off.
                assert!(!data.is_macro_def());

                for alternative in &mut data.alternatives {
                    self.replace_symbols(&mut alternative.expr.symbols);
                }
            }
        }
    }

    fn replace_symbols(&mut self, symbols: &mut [Symbol]) {
        for symbol in symbols {
            self.replace_symbol(symbol);
        }
    }

    fn replace_symbol(&mut self, symbol: &mut Symbol) {
        match *symbol {
            Symbol::Macro(ref mut m) => {
                for sym in &mut m.args {
                    self.replace_symbol(sym);
                }
            }
            Symbol::Expr(ref mut expr) => {
                self.replace_symbols(&mut expr.symbols);
            }
            Symbol::Repeat(ref mut repeat) => {
                self.replace_symbol(&mut repeat.symbol);
            }
            Symbol::Terminal(_) |
            Symbol::Nonterminal(_) => {
                return;
            }
            Symbol::Choose(ref mut sym) |
            Symbol::Name(_, ref mut sym) => {
                self.replace_symbol(sym);
                return;
            }
        }

        // only symbols we intend to expand fallthrough to here

        let key = intern(&symbol.canonical_form());
        let to_expand = mem::replace(symbol, Symbol::Nonterminal(key));
        if self.expansion_set.insert(key) {
            self.expansion_stack.push(to_expand);
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Macro expansion

    fn expand_macro_symbol(&mut self, msym: MacroSymbol) -> NormResult<GrammarItem> {
        let msym_name = intern(&msym.canonical_form());

        let mdef = match self.macro_defs.get(&msym.name) {
            Some(v) => v,
            None => return_err!(msym.span, "no macro definition found for `{}`", msym.name)
        };

        if mdef.args.len() != msym.args.len() {
            return_err!(msym.span, "expected {} arguments to `{}` but found {}",
                        mdef.args.len(), msym.name, msym.args.len());
        }

        let args: HashMap<InternedString, Symbol> =
            mdef.args.iter()
                     .cloned()
                     .zip(msym.args.into_iter())
                     .collect();

        let type_decl = mdef.type_decl.as_ref().map(|tr| self.macro_expand_type_ref(&args, tr));

        // due to the use of `try!`, it's a bit awkward to write this with an iterator
        let mut alternatives: Vec<Alternative> = vec![];

        for alternative in &mdef.alternatives {
            if !try!(self.evaluate_cond(&args, &alternative.condition)) {
                continue;
            }
            alternatives.push(Alternative {
                span: msym.span,
                expr: self.macro_expand_expr_symbol(&args, &alternative.expr),
                condition: None,
                action: alternative.action.clone(),
            });
        }

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: msym.span,
            name: msym_name,
            args: vec![],
            type_decl: type_decl,
            alternatives: alternatives
        }))
    }

    fn macro_expand_type_refs(&self,
                              args: &HashMap<InternedString, Symbol>,
                              type_refs: &[TypeRef])
                              -> Vec<TypeRef>
    {
        type_refs.iter().map(|tr| self.macro_expand_type_ref(args, tr)).collect()
    }

    fn macro_expand_type_ref(&self,
                             args: &HashMap<InternedString, Symbol>,
                             type_ref: &TypeRef)
                             -> TypeRef
    {
        match *type_ref {
            TypeRef::Tuple(ref trs) =>
                TypeRef::Tuple(self.macro_expand_type_refs(args, trs)),
            TypeRef::Nominal { ref path, ref types } =>
                TypeRef::Nominal { path: path.clone(),
                                   types: self.macro_expand_type_refs(args, types) },
            TypeRef::Lifetime(id) =>
                TypeRef::Lifetime(id),
            TypeRef::OfSymbol(ref sym) =>
                TypeRef::OfSymbol(sym.clone()),
            TypeRef::Id(id) => {
                match args.get(&id) {
                    Some(sym) => TypeRef::OfSymbol(sym.clone()),
                    None => TypeRef::Nominal { path: vec![id], types: vec![] },
                }
            }
        }
    }

    fn evaluate_cond(&self,
                     args: &HashMap<InternedString, Symbol>,
                     opt_cond: &Option<Condition>)
                     -> NormResult<bool>
    {
        if let Some(ref c) = *opt_cond {
            match args[&c.lhs] {
                Symbol::Terminal(lhs) => {
                    match c.op {
                        ConditionOp::Equals => Ok(lhs == c.rhs),
                        ConditionOp::NotEquals => Ok(lhs != c.rhs),
                        ConditionOp::Match => self.re_match(c.span, lhs, c.rhs),
                        ConditionOp::NotMatch => Ok(!try!(self.re_match(c.span, lhs, c.rhs))),
                    }
                }
                ref lhs => {
                    return_err!(
                        c.span,
                        "invalid condition LHS `{}`, expected a terminal, not `{}`", c.lhs, lhs);
                }
            }
        } else {
            Ok(true)
        }
    }

    fn re_match(&self, span: Span, lhs: InternedString, regex: InternedString) -> NormResult<bool> {
        read(|interner| {
            let re = match Regex::new(interner.data(regex)) {
                Ok(re) => re,
                Err(err) => return_err!(span, "invalid regular expression `{}`: {}", regex, err),
            };
            Ok(re.is_match(interner.data(lhs)))
        })
    }

    fn macro_expand_symbols(&self,
                            args: &HashMap<InternedString, Symbol>,
                            expr: &[Symbol])
                            -> Vec<Symbol>
    {
        expr.iter().map(|s| self.macro_expand_symbol(args, s)).collect()
    }

    fn macro_expand_expr_symbol(&self,
                                args: &HashMap<InternedString, Symbol>,
                                expr: &ExprSymbol)
                                -> ExprSymbol
    {
        ExprSymbol { span: expr.span, // FIXME derived span
                     symbols: self.macro_expand_symbols(args, &expr.symbols) }
    }

    fn macro_expand_symbol(&self,
                           args: &HashMap<InternedString, Symbol>,
                           symbol: &Symbol)
                           -> Symbol
    {
        match *symbol {
            Symbol::Expr(ref expr) =>
                Symbol::Expr(self.macro_expand_expr_symbol(args, expr)),
            Symbol::Terminal(id) =>
                Symbol::Terminal(id),
            Symbol::Nonterminal(id) =>
                match args.get(&id) {
                    Some(sym) => sym.clone(),
                    None => Symbol::Nonterminal(id),
                },
            Symbol::Macro(ref msym) =>
                Symbol::Macro(MacroSymbol {
                    name: msym.name,
                    args: self.macro_expand_symbols(args, &msym.args),
                    span: msym.span,
                }),
            Symbol::Repeat(ref r) =>
                Symbol::Repeat(Box::new(RepeatSymbol {
                    span: r.span,
                    op: r.op,
                    symbol: self.macro_expand_symbol(args, &r.symbol)
                })),
            Symbol::Choose(ref sym) =>
                Symbol::Choose(Box::new(self.macro_expand_symbol(args, sym))),
            Symbol::Name(id, ref sym) =>
                Symbol::Name(id, Box::new(self.macro_expand_symbol(args, sym))),
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_expr_symbol(&mut self, expr: ExprSymbol) -> NormResult<GrammarItem> {
        let name = intern(&expr.canonical_form());

        let ty_ref = match norm_util::analyze_expr(&expr) {
            Symbols::Named(names) => {
                let (ex_id, ex_sym) = names[0];
                return_err!(
                    expr.span,
                    "named symbols like `~{}:{}` are only allowed at the top-level of a nonterminal",
                    ex_id, ex_sym)
            }
            Symbols::Anon(syms) => {
                maybe_tuple(
                    syms.into_iter()
                        .cloned()
                        .map(TypeRef::OfSymbol)
                        .collect())
            }
        };

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: expr.span,
            name: name,
            args: vec![],
            type_decl: Some(ty_ref),
            alternatives: vec![Alternative { span: expr.span,
                                             expr: expr,
                                             condition: None,
                                             action: Some(format!("(~~)")) }]
        }))
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_repeat_symbol(&mut self, repeat: RepeatSymbol) -> NormResult<GrammarItem> {
        let name = intern(&repeat.canonical_form());
        let v = intern("v");
        let e = intern("e");

        let base_symbol_ty = TypeRef::OfSymbol(repeat.symbol.clone());

        match repeat.op {
            RepeatOp::Star => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X* =
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![],
                            },
                            condition: None,
                            action: Some(format!("vec![]"))
                        },

                        // X* = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Plus => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X+ = X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![repeat.symbol.clone()]
                            },
                            condition: None,
                            action: Some(format!("vec![~~]"))
                        },

                        // X+ = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Question => {
                let path = vec![intern("std"), intern("option"), intern("Option")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X? = X => Some(~~)
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![repeat.symbol.clone()]
                                      },
                                      condition: None,
                                      action: Some(format!("Some(~~)")) },

                        // X? = { => None; }
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![]
                                      },
                                      condition: None,
                                      action: Some(format!("None")) }]
                }))
            }
        }
    }

}

fn maybe_tuple(v: Vec<TypeRef>) -> TypeRef {
    if v.len() == 1 {
        v.into_iter().next().unwrap()
    } else {
        TypeRef::Tuple(v)
    }
}
<commit_msg>remove unnecessary counter<commit_after>use std::collections::{HashMap, HashSet};
use intern::{intern, read, InternedString};
use grammar::parse_tree::{Alternative, Condition, ConditionOp, ExprSymbol, Grammar, GrammarItem,
                          MacroSymbol, NonterminalData, RepeatOp, RepeatSymbol,
                          Span, Symbol, TypeRef};
use normalize::{NormResult, NormError};
use normalize::norm_util::{self, Symbols};
use regex::Regex;
use std::mem;

#[cfg(test)]
mod test;

pub fn expand_macros(input: Grammar) -> NormResult<Grammar> {
    let Grammar { span, type_name, items } = input;

    let (macro_defs, mut items): (Vec<_>, Vec<_>) =
        items.into_iter().partition(|mi| mi.is_macro_def());

    let macro_defs: HashMap<InternedString, NonterminalData> =
        macro_defs.into_iter()
                  .map(|md| match md {
                      GrammarItem::Nonterminal(data) => (data.name, data),
                      _ => unreachable!()
                  })
                  .collect();

    let mut expander = MacroExpander::new(macro_defs);
    try!(expander.expand(&mut items));

    Ok(Grammar { span: span, type_name: type_name, items: items })
}

struct MacroExpander {
    macro_defs: HashMap<InternedString, NonterminalData>,
    expansion_set: HashSet<InternedString>,
    expansion_stack: Vec<Symbol>,
}

impl MacroExpander {
    fn new(macro_defs: HashMap<InternedString, NonterminalData>) -> MacroExpander {
        MacroExpander {
            macro_defs: macro_defs,
            expansion_stack: Vec::new(),
            expansion_set: HashSet::new()
        }
    }

    fn expand(&mut self, items: &mut Vec<GrammarItem>) -> NormResult<()> {
        let mut counter = 0;
        loop {
            println!("Next round: counter={} items.len()={}",
                     counter, items.len());

            // Find any macro uses in items added since last round and
            // replace them in place with the expanded version:
            for item in &mut items[counter..] {
                self.replace_item(item);
            }
            counter = items.len();

            // No more expansion to do.
            if self.expansion_stack.is_empty() {
                return Ok(());
            }

            // Drain expansion stack:
            while let Some(sym) = self.expansion_stack.pop() {
                println!("Drained {:?}", sym);
                match sym {
                    Symbol::Macro(msym) =>
                        items.push(try!(self.expand_macro_symbol(msym))),
                    Symbol::Expr(expr) =>
                        items.push(try!(self.expand_expr_symbol(expr))),
                    Symbol::Repeat(repeat) =>
                        items.push(try!(self.expand_repeat_symbol(*repeat))),
                    _ =>
                        assert!(false, "don't know how to expand `{:?}`", sym)
                }
            }
        }
    }

    fn replace_item(&mut self, item: &mut GrammarItem) {
        match *item {
            GrammarItem::TokenType(..) => { }
            GrammarItem::Nonterminal(ref mut data) => {
                // Should not encounter macro definitions here,
                // they've already been siphoned off.
                assert!(!data.is_macro_def());

                for alternative in &mut data.alternatives {
                    self.replace_symbols(&mut alternative.expr.symbols);
                }
            }
        }
    }

    fn replace_symbols(&mut self, symbols: &mut [Symbol]) {
        for symbol in symbols {
            self.replace_symbol(symbol);
        }
    }

    fn replace_symbol(&mut self, symbol: &mut Symbol) {
        match *symbol {
            Symbol::Macro(ref mut m) => {
                for sym in &mut m.args {
                    self.replace_symbol(sym);
                }
            }
            Symbol::Expr(ref mut expr) => {
                self.replace_symbols(&mut expr.symbols);
            }
            Symbol::Repeat(ref mut repeat) => {
                self.replace_symbol(&mut repeat.symbol);
            }
            Symbol::Terminal(_) |
            Symbol::Nonterminal(_) => {
                return;
            }
            Symbol::Choose(ref mut sym) |
            Symbol::Name(_, ref mut sym) => {
                self.replace_symbol(sym);
                return;
            }
        }

        // only symbols we intend to expand fallthrough to here

        let key = intern(&symbol.canonical_form());
        let to_expand = mem::replace(symbol, Symbol::Nonterminal(key));
        if self.expansion_set.insert(key) {
            self.expansion_stack.push(to_expand);
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Macro expansion

    fn expand_macro_symbol(&mut self, msym: MacroSymbol) -> NormResult<GrammarItem> {
        let msym_name = intern(&msym.canonical_form());

        let mdef = match self.macro_defs.get(&msym.name) {
            Some(v) => v,
            None => return_err!(msym.span, "no macro definition found for `{}`", msym.name)
        };

        if mdef.args.len() != msym.args.len() {
            return_err!(msym.span, "expected {} arguments to `{}` but found {}",
                        mdef.args.len(), msym.name, msym.args.len());
        }

        let args: HashMap<InternedString, Symbol> =
            mdef.args.iter()
                     .cloned()
                     .zip(msym.args.into_iter())
                     .collect();

        let type_decl = mdef.type_decl.as_ref().map(|tr| self.macro_expand_type_ref(&args, tr));

        // due to the use of `try!`, it's a bit awkward to write this with an iterator
        let mut alternatives: Vec<Alternative> = vec![];

        for alternative in &mdef.alternatives {
            if !try!(self.evaluate_cond(&args, &alternative.condition)) {
                continue;
            }
            alternatives.push(Alternative {
                span: msym.span,
                expr: self.macro_expand_expr_symbol(&args, &alternative.expr),
                condition: None,
                action: alternative.action.clone(),
            });
        }

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: msym.span,
            name: msym_name,
            args: vec![],
            type_decl: type_decl,
            alternatives: alternatives
        }))
    }

    fn macro_expand_type_refs(&self,
                              args: &HashMap<InternedString, Symbol>,
                              type_refs: &[TypeRef])
                              -> Vec<TypeRef>
    {
        type_refs.iter().map(|tr| self.macro_expand_type_ref(args, tr)).collect()
    }

    fn macro_expand_type_ref(&self,
                             args: &HashMap<InternedString, Symbol>,
                             type_ref: &TypeRef)
                             -> TypeRef
    {
        match *type_ref {
            TypeRef::Tuple(ref trs) =>
                TypeRef::Tuple(self.macro_expand_type_refs(args, trs)),
            TypeRef::Nominal { ref path, ref types } =>
                TypeRef::Nominal { path: path.clone(),
                                   types: self.macro_expand_type_refs(args, types) },
            TypeRef::Lifetime(id) =>
                TypeRef::Lifetime(id),
            TypeRef::OfSymbol(ref sym) =>
                TypeRef::OfSymbol(sym.clone()),
            TypeRef::Id(id) => {
                match args.get(&id) {
                    Some(sym) => TypeRef::OfSymbol(sym.clone()),
                    None => TypeRef::Nominal { path: vec![id], types: vec![] },
                }
            }
        }
    }

    fn evaluate_cond(&self,
                     args: &HashMap<InternedString, Symbol>,
                     opt_cond: &Option<Condition>)
                     -> NormResult<bool>
    {
        if let Some(ref c) = *opt_cond {
            match args[&c.lhs] {
                Symbol::Terminal(lhs) => {
                    match c.op {
                        ConditionOp::Equals => Ok(lhs == c.rhs),
                        ConditionOp::NotEquals => Ok(lhs != c.rhs),
                        ConditionOp::Match => self.re_match(c.span, lhs, c.rhs),
                        ConditionOp::NotMatch => Ok(!try!(self.re_match(c.span, lhs, c.rhs))),
                    }
                }
                ref lhs => {
                    return_err!(
                        c.span,
                        "invalid condition LHS `{}`, expected a terminal, not `{}`", c.lhs, lhs);
                }
            }
        } else {
            Ok(true)
        }
    }

    fn re_match(&self, span: Span, lhs: InternedString, regex: InternedString) -> NormResult<bool> {
        read(|interner| {
            let re = match Regex::new(interner.data(regex)) {
                Ok(re) => re,
                Err(err) => return_err!(span, "invalid regular expression `{}`: {}", regex, err),
            };
            Ok(re.is_match(interner.data(lhs)))
        })
    }

    fn macro_expand_symbols(&self,
                            args: &HashMap<InternedString, Symbol>,
                            expr: &[Symbol])
                            -> Vec<Symbol>
    {
        expr.iter().map(|s| self.macro_expand_symbol(args, s)).collect()
    }

    fn macro_expand_expr_symbol(&self,
                                args: &HashMap<InternedString, Symbol>,
                                expr: &ExprSymbol)
                                -> ExprSymbol
    {
        ExprSymbol { span: expr.span, // FIXME derived span
                     symbols: self.macro_expand_symbols(args, &expr.symbols) }
    }

    fn macro_expand_symbol(&self,
                           args: &HashMap<InternedString, Symbol>,
                           symbol: &Symbol)
                           -> Symbol
    {
        match *symbol {
            Symbol::Expr(ref expr) =>
                Symbol::Expr(self.macro_expand_expr_symbol(args, expr)),
            Symbol::Terminal(id) =>
                Symbol::Terminal(id),
            Symbol::Nonterminal(id) =>
                match args.get(&id) {
                    Some(sym) => sym.clone(),
                    None => Symbol::Nonterminal(id),
                },
            Symbol::Macro(ref msym) =>
                Symbol::Macro(MacroSymbol {
                    name: msym.name,
                    args: self.macro_expand_symbols(args, &msym.args),
                    span: msym.span,
                }),
            Symbol::Repeat(ref r) =>
                Symbol::Repeat(Box::new(RepeatSymbol {
                    span: r.span,
                    op: r.op,
                    symbol: self.macro_expand_symbol(args, &r.symbol)
                })),
            Symbol::Choose(ref sym) =>
                Symbol::Choose(Box::new(self.macro_expand_symbol(args, sym))),
            Symbol::Name(id, ref sym) =>
                Symbol::Name(id, Box::new(self.macro_expand_symbol(args, sym))),
        }
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_expr_symbol(&mut self, expr: ExprSymbol) -> NormResult<GrammarItem> {
        let name = intern(&expr.canonical_form());

        let ty_ref = match norm_util::analyze_expr(&expr) {
            Symbols::Named(names) => {
                let (ex_id, ex_sym) = names[0];
                return_err!(
                    expr.span,
                    "named symbols like `~{}:{}` are only allowed at the top-level of a nonterminal",
                    ex_id, ex_sym)
            }
            Symbols::Anon(syms) => {
                maybe_tuple(
                    syms.into_iter()
                        .cloned()
                        .map(TypeRef::OfSymbol)
                        .collect())
            }
        };

        Ok(GrammarItem::Nonterminal(NonterminalData {
            span: expr.span,
            name: name,
            args: vec![],
            type_decl: Some(ty_ref),
            alternatives: vec![Alternative { span: expr.span,
                                             expr: expr,
                                             condition: None,
                                             action: Some(format!("(~~)")) }]
        }))
    }

    ///////////////////////////////////////////////////////////////////////////
    // Expr expansion

    fn expand_repeat_symbol(&mut self, repeat: RepeatSymbol) -> NormResult<GrammarItem> {
        let name = intern(&repeat.canonical_form());
        let v = intern("v");
        let e = intern("e");

        let base_symbol_ty = TypeRef::OfSymbol(repeat.symbol.clone());

        match repeat.op {
            RepeatOp::Star => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X* =
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![],
                            },
                            condition: None,
                            action: Some(format!("vec![]"))
                        },

                        // X* = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Plus => {
                let path = vec![intern("std"), intern("vec"), intern("Vec")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X+ = X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![repeat.symbol.clone()]
                            },
                            condition: None,
                            action: Some(format!("vec![~~]"))
                        },

                        // X+ = ~v:X+ ~e:X
                        Alternative {
                            span: repeat.span,
                            expr: ExprSymbol {
                                span: repeat.span,
                                symbols: vec![Symbol::Name(v, Box::new(Symbol::Nonterminal(name))),
                                              Symbol::Name(e, Box::new(repeat.symbol.clone()))]
                            },
                            condition: None,
                            action: Some(format!("{{ let mut v = v; v.push(e); v }}"))
                        }],
                }))
            }

            RepeatOp::Question => {
                let path = vec![intern("std"), intern("option"), intern("Option")];
                let ty_ref = TypeRef::Nominal { path: path, types: vec![base_symbol_ty] };

                Ok(GrammarItem::Nonterminal(NonterminalData {
                    span: repeat.span,
                    name: name,
                    args: vec![],
                    type_decl: Some(ty_ref),
                    alternatives: vec![
                        // X? = X => Some(~~)
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![repeat.symbol.clone()]
                                      },
                                      condition: None,
                                      action: Some(format!("Some(~~)")) },

                        // X? = { => None; }
                        Alternative { span: repeat.span,
                                      expr: ExprSymbol {
                                          span: repeat.span,
                                          symbols: vec![]
                                      },
                                      condition: None,
                                      action: Some(format!("None")) }]
                }))
            }
        }
    }

}

fn maybe_tuple(v: Vec<TypeRef>) -> TypeRef {
    if v.len() == 1 {
        v.into_iter().next().unwrap()
    } else {
        TypeRef::Tuple(v)
    }
}
<|endoftext|>


<commit_before>use std::mem;
use libc::{uint32_t, c_void};

pub use sys::timer as ll;

pub fn get_ticks() -> uint {
    unsafe { ll::SDL_GetTicks() as uint }
}

pub fn get_performance_counter() -> u64 {
    unsafe { ll::SDL_GetPerformanceCounter() }
}

pub fn get_performance_frequency() -> u64 {
    unsafe { ll::SDL_GetPerformanceFrequency() }
}

pub fn delay(ms: uint) {
    unsafe { ll::SDL_Delay(ms as u32) }
}

pub struct Timer<'a> {
    delay: uint,
    raw: ll::SDL_TimerID,
    closure: Box<FnMut() -> uint + 'a>,
}

impl<'a> Timer<'a> {
    pub fn new(delay: uint, callback: Box<FnMut() -> uint + 'a>) -> Timer<'a> {
        Timer { delay: delay, raw: 0, closure: callback }
    }

    pub fn start(&mut self) {
        unsafe {
            let timer_id = ll::SDL_AddTimer(
                    self.delay as u32,
                    Some(c_timer_callback as
                        extern "C" fn (
                            _interval: uint32_t,
                            param: *const c_void
                        ) -> uint32_t),
                    mem::transmute(&self.closure)
                );
            self.raw = timer_id;
        }
    }

    pub fn remove(&mut self) -> bool {
        let ret = unsafe { ll::SDL_RemoveTimer(self.raw) };
        if self.raw != 0 {
            self.raw = 0
        }
        ret == 1
    }
}

#[unsafe_destructor]
impl<'a> Drop for Timer<'a> {
    fn drop(&mut self) {
        let ret = unsafe { ll::SDL_RemoveTimer(self.raw) };
        if ret != 1 {
            println!("error dropping timer {}, maybe already removed.", self.raw);
        }
    }
}

extern "C" fn c_timer_callback(_interval: uint32_t, param: *const c_void) -> uint32_t {
    let f: &mut Box<FnMut() -> uint> = unsafe { mem::transmute(param) };
    (*f)() as uint32_t
}

#[test]
fn test_timer() {
    use std::sync::{Arc, Mutex};

    let local_num: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
    let timer_num = local_num.clone();
    {
        let f: Box<FnMut() -> uint> = box |&mut:| {
            let mut num = timer_num.lock().unwrap();
            *num = *num + 1;
            10
        };
        let mut timer = Timer::new(10, f);
        timer.start();
        delay(100);
        let num = local_num.lock().unwrap();
        assert!(*num == 9);
    }

    // Check that timer has stopped
    delay(100);
    let num = local_num.lock().unwrap();
    assert!(*num == 9);
}
<commit_msg>Pass a reference to a Box'ed unboxed closure, in Timer.<commit_after>use std::mem;
use libc::{uint32_t, c_void};

pub use sys::timer as ll;

pub fn get_ticks() -> uint {
    unsafe { ll::SDL_GetTicks() as uint }
}

pub fn get_performance_counter() -> u64 {
    unsafe { ll::SDL_GetPerformanceCounter() }
}

pub fn get_performance_frequency() -> u64 {
    unsafe { ll::SDL_GetPerformanceFrequency() }
}

pub fn delay(ms: uint) {
    unsafe { ll::SDL_Delay(ms as u32) }
}

pub struct Timer<'a> {
    delay: uint,
    raw: ll::SDL_TimerID,
    closure: &'a Box<FnMut() -> uint + 'a>,
}

impl<'a> Timer<'a> {
    pub fn new(delay: uint, callback: &'a Box<FnMut() -> uint + 'a>) -> Timer<'a> {
        Timer { delay: delay, raw: 0, closure: callback }
    }

    pub fn start(&mut self) {
        unsafe {
            let timer_id = ll::SDL_AddTimer(
                    self.delay as u32,
                    Some(c_timer_callback as
                        extern "C" fn (
                            _interval: uint32_t,
                            param: *const c_void
                        ) -> uint32_t),
                    mem::transmute(self.closure)
                );
            self.raw = timer_id;
        }
    }

    pub fn remove(&mut self) -> bool {
        let ret = unsafe { ll::SDL_RemoveTimer(self.raw) };
        if self.raw != 0 {
            self.raw = 0
        }
        ret == 1
    }
}

#[unsafe_destructor]
impl<'a> Drop for Timer<'a> {
    fn drop(&mut self) {
        let ret = unsafe { ll::SDL_RemoveTimer(self.raw) };
        if ret != 1 {
            println!("error dropping timer {}, maybe already removed.", self.raw);
        }
    }
}

extern "C" fn c_timer_callback(_interval: uint32_t, param: *const c_void) -> uint32_t {
    let f: &mut Box<FnMut() -> uint> = unsafe { mem::transmute(param) };
    (*f)() as uint32_t
}

#[test]
fn test_timer_1() {
    use std::sync::{Arc, Mutex};

    let local_num: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
    let timer_num = local_num.clone();
    {
        let f: Box<FnMut() -> uint> = box |&mut:| {
            let mut num = timer_num.lock().unwrap();
            *num = *num + 1;
            10
        };
        let mut timer = Timer::new(10, &f);
        timer.start();
        delay(100);
        let num = local_num.lock().unwrap();
        assert!(*num == 9);
    }

    // Check that timer has stopped
    delay(100);
    let num = local_num.lock().unwrap();
    assert!(*num == 9);
}

#[test]
fn test_timer_2() {
    // Check that the closure lives long enough outside the block where
    // the timer was started.
    let f: Box<FnMut() -> uint> = box |&:| { 0 };
    let _ = {
        let mut timer = Timer::new(1000, &f);
        timer.start();
        timer
    };
    delay(200);
    delay(200);
    delay(200);
    delay(200);
    delay(200);
    delay(200);
}
<|endoftext|>


<commit_before>use std::time::{Duration, SystemTime, Instant};
use std::convert::TryFrom;

use crate::stacked_borrows::Tag;
use crate::*;
use helpers::{immty_from_int_checked, immty_from_uint_checked};

use rustc_middle::ty::layout::LayoutOf;

/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
    time.duration_since(SystemTime::UNIX_EPOCH)
        .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported").into())
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn clock_gettime(
        &mut self,
        clk_id_op: OpTy<'tcx, Tag>,
        tp_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("linux", "clock_gettime");
        this.check_no_isolation("clock_gettime")?;

        let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
        let tp = this.deref_operand(tp_op)?;

        let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? {
            system_time_to_duration(&SystemTime::now())?
        } else if clk_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
            // Absolute time does not matter, only relative time does, so we can just
            // use our own time anchor here.
            Instant::now().duration_since(this.machine.time_anchor)
        } else {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        };

        let tv_sec = duration.as_secs();
        let tv_nsec = duration.subsec_nanos();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_nsec, this.libc_ty_layout("c_long")?)?,
        ];

        this.write_packed_immediates(tp, &imms)?;

        Ok(0)
    }

    fn gettimeofday(
        &mut self,
        tv_op: OpTy<'tcx, Tag>,
        tz_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("macos", "gettimeofday");
        this.check_no_isolation("gettimeofday")?;

        // Using tz is obsolete and should always be null
        let tz = this.read_scalar(tz_op)?.not_undef()?;
        if !this.is_null(tz)? {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        }

        let tv = this.deref_operand(tv_op)?;

        let duration = system_time_to_duration(&SystemTime::now())?;
        let tv_sec = duration.as_secs();
        let tv_usec = duration.subsec_micros();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_usec, this.libc_ty_layout("suseconds_t")?)?,
        ];

        this.write_packed_immediates(tv, &imms)?;

        Ok(0)
    }

    #[allow(non_snake_case)]
    fn GetSystemTimeAsFileTime(&mut self, LPFILETIME_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "GetSystemTimeAsFileTime");
        this.check_no_isolation("GetSystemTimeAsFileTime")?;

        let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC")?;
        let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC")?;
        let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH")?;
        let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
        let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;

        let duration = system_time_to_duration(&SystemTime::now())?
            .checked_add(Duration::from_secs(SECONDS_TO_UNIX_EPOCH))
            .unwrap();
        let duration_ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
            .map_err(|_| err_unsup_format!("programs running longer than 2^64 ticks are not supported"))?;

        let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap();
        let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap();
        let DWORD_tylayout = this.layout_of(this.tcx.types.u32)?;
        let imms = [
            immty_from_uint_checked(dwLowDateTime, DWORD_tylayout)?,
            immty_from_uint_checked(dwHighDateTime, DWORD_tylayout)?,
        ];
        this.write_packed_immediates(this.deref_operand(LPFILETIME_op)?, &imms)?;
        Ok(())
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceCounter(&mut self, lpPerformanceCount_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceCounter");
        this.check_no_isolation("QueryPerformanceCounter")?;

        // QPC uses a hardware counter as its basis.
        // Miri will assume that the machine's hardware counter has a resolution of 1 nanosecond.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        let qpc = i64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported"))?;
        this.write_scalar(Scalar::from_i64(qpc), this.deref_operand(lpPerformanceCount_op)?.into())?;
        Ok(-1) // return non-zero on success
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceFrequency(&mut self, lpFrequency_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceFrequency");
        this.check_no_isolation("QueryPerformanceFrequency")?;

        // Retrieves the frequency of the hardware performance counter.
        // The frequency of the performance counter is fixed at system boot and
        // is consistent across all processors.
        // Miri will assume that the frequency of
        // the machine's hardware performance counter is 1 GHz ( = 1 x 10^9 Hz).
        this.write_scalar(Scalar::from_i64(1_000_000_000), this.deref_operand(lpFrequency_op)?.into())?;
        Ok(-1) // Return non-zero on success
    }

    fn mach_absolute_time(&self) -> InterpResult<'tcx, u64> {
        let this = self.eval_context_ref();

        this.assert_target_os("macos", "mach_absolute_time");
        this.check_no_isolation("mach_absolute_time")?;

        // This returns a u64, with time units determined dynamically by `mach_timebase_info`.
        // We return plain nanoseconds.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        u64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into())
    }
}
<commit_msg>fix incorrect error message in shim 'QueryPerformanceCounter'<commit_after>use std::time::{Duration, SystemTime, Instant};
use std::convert::TryFrom;

use crate::stacked_borrows::Tag;
use crate::*;
use helpers::{immty_from_int_checked, immty_from_uint_checked};

use rustc_middle::ty::layout::LayoutOf;

/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
    time.duration_since(SystemTime::UNIX_EPOCH)
        .map_err(|_| err_unsup_format!("times before the Unix epoch are not supported").into())
}

impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn clock_gettime(
        &mut self,
        clk_id_op: OpTy<'tcx, Tag>,
        tp_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("linux", "clock_gettime");
        this.check_no_isolation("clock_gettime")?;

        let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
        let tp = this.deref_operand(tp_op)?;

        let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? {
            system_time_to_duration(&SystemTime::now())?
        } else if clk_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
            // Absolute time does not matter, only relative time does, so we can just
            // use our own time anchor here.
            Instant::now().duration_since(this.machine.time_anchor)
        } else {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        };

        let tv_sec = duration.as_secs();
        let tv_nsec = duration.subsec_nanos();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_nsec, this.libc_ty_layout("c_long")?)?,
        ];

        this.write_packed_immediates(tp, &imms)?;

        Ok(0)
    }

    fn gettimeofday(
        &mut self,
        tv_op: OpTy<'tcx, Tag>,
        tz_op: OpTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("macos", "gettimeofday");
        this.check_no_isolation("gettimeofday")?;

        // Using tz is obsolete and should always be null
        let tz = this.read_scalar(tz_op)?.not_undef()?;
        if !this.is_null(tz)? {
            let einval = this.eval_libc("EINVAL")?;
            this.set_last_error(einval)?;
            return Ok(-1);
        }

        let tv = this.deref_operand(tv_op)?;

        let duration = system_time_to_duration(&SystemTime::now())?;
        let tv_sec = duration.as_secs();
        let tv_usec = duration.subsec_micros();

        let imms = [
            immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?,
            immty_from_int_checked(tv_usec, this.libc_ty_layout("suseconds_t")?)?,
        ];

        this.write_packed_immediates(tv, &imms)?;

        Ok(0)
    }

    #[allow(non_snake_case)]
    fn GetSystemTimeAsFileTime(&mut self, LPFILETIME_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "GetSystemTimeAsFileTime");
        this.check_no_isolation("GetSystemTimeAsFileTime")?;

        let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC")?;
        let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC")?;
        let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH")?;
        let NANOS_PER_INTERVAL = NANOS_PER_SEC / INTERVALS_PER_SEC;
        let SECONDS_TO_UNIX_EPOCH = INTERVALS_TO_UNIX_EPOCH / INTERVALS_PER_SEC;

        let duration = system_time_to_duration(&SystemTime::now())?
            .checked_add(Duration::from_secs(SECONDS_TO_UNIX_EPOCH))
            .unwrap();
        let duration_ticks = u64::try_from(duration.as_nanos() / u128::from(NANOS_PER_INTERVAL))
            .map_err(|_| err_unsup_format!("programs running longer than 2^64 ticks are not supported"))?;

        let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap();
        let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap();
        let DWORD_tylayout = this.layout_of(this.tcx.types.u32)?;
        let imms = [
            immty_from_uint_checked(dwLowDateTime, DWORD_tylayout)?,
            immty_from_uint_checked(dwHighDateTime, DWORD_tylayout)?,
        ];
        this.write_packed_immediates(this.deref_operand(LPFILETIME_op)?, &imms)?;
        Ok(())
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceCounter(&mut self, lpPerformanceCount_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceCounter");
        this.check_no_isolation("QueryPerformanceCounter")?;

        // QPC uses a hardware counter as its basis.
        // Miri will assume that the machine's hardware counter has a resolution of 1 nanosecond.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        let qpc = i64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported"))?;
        this.write_scalar(Scalar::from_i64(qpc), this.deref_operand(lpPerformanceCount_op)?.into())?;
        Ok(-1) // return non-zero on success
    }

    #[allow(non_snake_case)]
    fn QueryPerformanceFrequency(&mut self, lpFrequency_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {
        let this = self.eval_context_mut();

        this.assert_target_os("windows", "QueryPerformanceFrequency");
        this.check_no_isolation("QueryPerformanceFrequency")?;

        // Retrieves the frequency of the hardware performance counter.
        // The frequency of the performance counter is fixed at system boot and
        // is consistent across all processors.
        // Miri will assume that the frequency of
        // the machine's hardware performance counter is 1 GHz ( = 1 x 10^9 Hz).
        this.write_scalar(Scalar::from_i64(1_000_000_000), this.deref_operand(lpFrequency_op)?.into())?;
        Ok(-1) // Return non-zero on success
    }

    fn mach_absolute_time(&self) -> InterpResult<'tcx, u64> {
        let this = self.eval_context_ref();

        this.assert_target_os("macos", "mach_absolute_time");
        this.check_no_isolation("mach_absolute_time")?;

        // This returns a u64, with time units determined dynamically by `mach_timebase_info`.
        // We return plain nanoseconds.
        let duration = Instant::now().duration_since(this.machine.time_anchor);
        u64::try_from(duration.as_nanos())
            .map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into())
    }
}
<|endoftext|>


<commit_before>use cgmath::*;

use std::collections::VecDeque;
use std::ops::{Index, IndexMut};

use terrain::tile_cache::{Priority, TileCache};
use utils::math::BoundingBox;

#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct NodeId(u32);
impl NodeId {
    pub fn root() -> Self {
        NodeId(0)
    }
    pub fn index(&self) -> usize {
        self.0 as usize
    }
}
impl Index<NodeId> for Vec<Node> {
    type Output = Node;
    fn index(&self, id: NodeId) -> &Node {
        &self[id.0 as usize]
    }
}
impl IndexMut<NodeId> for Vec<Node> {
    fn index_mut(&mut self, id: NodeId) -> &mut Node {
        &mut self[id.0 as usize]
    }
}

const NUM_LAYERS: usize = 3;

pub struct Node {
    level: u8,
    #[allow(unused)]
    parent: Option<NodeId>,
    children: [Option<NodeId>; 4],

    bounds: BoundingBox,
    min_distance: f32,

    size: i32,
    center: Point2<i32>,

    /// Index of this node in the tile list.
    tile_indices: [Option<u32>; NUM_LAYERS],

    /// How much this node is needed for the current frame. Nodes with priority less than 1.0 will
    /// not be rendered (they are too detailed).
    priority: Priority,
}
impl Node {
    pub fn priority(&self) -> Priority {
        self.priority
    }
}

pub struct QuadTree {
    /// List of nodes in the `QuadTree`. The root is always at index 0.
    nodes: Vec<Node>,

    /// List of nodes that will be rendered.
    visible_nodes: Vec<NodeId>,

    /// Cache holding nearby tiles for each layer.
    tile_cache_layers: [TileCache; NUM_LAYERS],
}

#[allow(unused)]
impl QuadTree {
    pub fn new(side_length: f32, playable_radius: f32, max_level: u8) -> Self {
        let node = Node {
            level: 0,
            parent: None,
            children: [None; 4],
            bounds: BoundingBox {
                min: Point3::new(-side_length * 0.5, 0.0, -side_length * 0.5),
                max: Point3::new(side_length * 0.5, 16000.0, side_length * 0.5),
            },
            min_distance: side_length * 4.,
            center: Point2::origin(),
            size: 1 << 31,
            priority: Priority::none(),
            tile_indices: [None; NUM_LAYERS],
        };

        let mut nodes = vec![node];
        let mut pending = VecDeque::new();
        pending.push_back(NodeId::root());

        while let Some(parent) = pending.pop_front() {
            if nodes[parent].level >= max_level {
                continue;
            }

            let min_distance = nodes[parent].min_distance * 0.5;
            let min = nodes[parent].bounds.min;
            let max = nodes[parent].bounds.max;
            let center = Point3::midpoint(min, max);
            let bounds = [
                BoundingBox::new(
                    Point3::new(min.x, min.y, min.z),
                    Point3::new(center.x, max.y, center.z),
                ),
                BoundingBox::new(
                    Point3::new(center.x, min.y, min.z),
                    Point3::new(max.x, max.y, center.z),
                ),
                BoundingBox::new(
                    Point3::new(min.x, min.y, center.z),
                    Point3::new(center.x, max.y, max.z),
                ),
                BoundingBox::new(
                    Point3::new(center.x, min.y, center.z),
                    Point3::new(max.x, max.y, max.z),
                ),
            ];

            let offsets = [
                Vector2::new(-1, -1),
                Vector2::new(1, -1),
                Vector2::new(-1, 1),
                Vector2::new(1, 1),
            ];

            for i in 0..4 {
                if bounds[i].distance(Point3::origin()) < playable_radius + min_distance {
                    let child = NodeId(nodes.len() as u32);
                    let child_node = Node {
                        level: nodes[parent].level + 1,
                        parent: Some(parent),
                        children: [None; 4],
                        bounds: bounds[i],
                        min_distance: nodes[parent].min_distance * 0.5,
                        size: nodes[parent].size / 2,
                        center: Point2::from_vec(
                            nodes[parent].center.to_vec() - offsets[i] * nodes[parent].size / 4,
                        ),
                        priority: Priority::none(),
                        tile_indices: [None; NUM_LAYERS],
                    };

                    nodes.push(child_node);
                    nodes[parent].children[i] = Some(child);
                    pending.push_back(child);
                }
            }
        }

        Self {
            nodes,
            visible_nodes: Vec::new(),
            tile_cache_layers: [
                TileCache::new(1024),
                TileCache::new(512),
                TileCache::new(96),
            ],
        }
    }

    fn update_priorities(&mut self, camera: Point3<f32>) {
        for node in self.nodes.iter_mut() {
            node.priority = Priority::from_f32(
                node.bounds.square_distance(camera).max(1.0) /
                    (node.min_distance * node.min_distance),
            );
        }
        for cache_layer in self.tile_cache_layers.iter_mut() {
            cache_layer.update_priorities(&mut self.nodes);
        }
    }

    pub fn update(&mut self, camera: Point3<f32>) {
        self.update_priorities(camera);

        self.breadth_first(|qt, id| {
            if qt.nodes[id].priority < Priority::cutoff() {
                return false;
            }

            for layer in 0..NUM_LAYERS {
                if qt.nodes[id].tile_indices[layer].is_some() &&
                    !qt.tile_cache_layers[layer].contains(id)
                {
                    qt.tile_cache_layers[layer].add_missing((qt.nodes[id].priority, id));
                }
            }
            true
        });

        for cache_layer in self.tile_cache_layers.iter_mut() {
            cache_layer.load_missing(&mut self.nodes);
        }
    }

    fn breadth_first<F>(&mut self, mut visit: F)
    where
        F: FnMut(&mut Self, NodeId) -> bool,
    {
        let mut pending = VecDeque::new();
        if visit(self, NodeId::root()) {
            pending.push_back(NodeId::root());
        }
        while let Some(id) = pending.pop_front() {
            for i in 0..4 {
                if let Some(child) = self.nodes[id].children[i] {
                    if visit(self, child) {
                        pending.push_back(child);
                    }
                }
            }
        }
    }
}
<commit_msg>Compute visible nodes of quadtree<commit_after>use cgmath::*;

use std::collections::VecDeque;
use std::ops::{Index, IndexMut};

use terrain::tile_cache::{Priority, TileCache};
use utils::math::BoundingBox;

#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
pub struct NodeId(u32);
impl NodeId {
    pub fn root() -> Self {
        NodeId(0)
    }
    pub fn index(&self) -> usize {
        self.0 as usize
    }
}
impl Index<NodeId> for Vec<Node> {
    type Output = Node;
    fn index(&self, id: NodeId) -> &Node {
        &self[id.0 as usize]
    }
}
impl IndexMut<NodeId> for Vec<Node> {
    fn index_mut(&mut self, id: NodeId) -> &mut Node {
        &mut self[id.0 as usize]
    }
}

const NUM_LAYERS: usize = 3;

pub struct Node {
    level: u8,
    #[allow(unused)]
    parent: Option<NodeId>,
    children: [Option<NodeId>; 4],

    bounds: BoundingBox,
    min_distance: f32,

    size: i32,
    center: Point2<i32>,

    /// Index of this node in the tile list.
    tile_indices: [Option<u32>; NUM_LAYERS],

    /// How much this node is needed for the current frame. Nodes with priority less than 1.0 will
    /// not be rendered (they are too detailed).
    priority: Priority,

    visible: bool,
}
impl Node {
    pub fn priority(&self) -> Priority {
        self.priority
    }
}

pub struct QuadTree {
    /// List of nodes in the `QuadTree`. The root is always at index 0.
    nodes: Vec<Node>,

    /// List of nodes that will be rendered.
    visible_nodes: Vec<NodeId>,

    /// Cache holding nearby tiles for each layer.
    tile_cache_layers: [TileCache; NUM_LAYERS],
}

#[allow(unused)]
impl QuadTree {
    pub fn new(side_length: f32, playable_radius: f32, max_level: u8) -> Self {
        let node = Node {
            level: 0,
            parent: None,
            children: [None; 4],
            bounds: BoundingBox {
                min: Point3::new(-side_length * 0.5, 0.0, -side_length * 0.5),
                max: Point3::new(side_length * 0.5, 16000.0, side_length * 0.5),
            },
            min_distance: side_length * 4.,
            center: Point2::origin(),
            size: 1 << 31,
            priority: Priority::none(),
            tile_indices: [None; NUM_LAYERS],
            visible: false,
        };

        let mut nodes = vec![node];
        let mut pending = VecDeque::new();
        pending.push_back(NodeId::root());

        while let Some(parent) = pending.pop_front() {
            if nodes[parent].level >= max_level {
                continue;
            }

            let min_distance = nodes[parent].min_distance * 0.5;
            let min = nodes[parent].bounds.min;
            let max = nodes[parent].bounds.max;
            let center = Point3::midpoint(min, max);
            let bounds = [
                BoundingBox::new(
                    Point3::new(min.x, min.y, min.z),
                    Point3::new(center.x, max.y, center.z),
                ),
                BoundingBox::new(
                    Point3::new(center.x, min.y, min.z),
                    Point3::new(max.x, max.y, center.z),
                ),
                BoundingBox::new(
                    Point3::new(min.x, min.y, center.z),
                    Point3::new(center.x, max.y, max.z),
                ),
                BoundingBox::new(
                    Point3::new(center.x, min.y, center.z),
                    Point3::new(max.x, max.y, max.z),
                ),
            ];

            let offsets = [
                Vector2::new(-1, -1),
                Vector2::new(1, -1),
                Vector2::new(-1, 1),
                Vector2::new(1, 1),
            ];

            for i in 0..4 {
                if bounds[i].distance(Point3::origin()) < playable_radius + min_distance {
                    let child = NodeId(nodes.len() as u32);
                    let child_node = Node {
                        level: nodes[parent].level + 1,
                        parent: Some(parent),
                        children: [None; 4],
                        bounds: bounds[i],
                        min_distance: nodes[parent].min_distance * 0.5,
                        size: nodes[parent].size / 2,
                        center: Point2::from_vec(
                            nodes[parent].center.to_vec() - offsets[i] * nodes[parent].size / 4,
                        ),
                        priority: Priority::none(),
                        tile_indices: [None; NUM_LAYERS],
                        visible: false,
                    };

                    nodes.push(child_node);
                    nodes[parent].children[i] = Some(child);
                    pending.push_back(child);
                }
            }
        }

        Self {
            nodes,
            visible_nodes: Vec::new(),
            tile_cache_layers: [
                TileCache::new(1024),
                TileCache::new(512),
                TileCache::new(96),
            ],
        }
    }

    fn update_priorities(&mut self, camera: Point3<f32>) {
        for node in self.nodes.iter_mut() {
            node.priority = Priority::from_f32(
                node.bounds.square_distance(camera).max(1.0) /
                    (node.min_distance * node.min_distance),
            );
        }
        for cache_layer in self.tile_cache_layers.iter_mut() {
            cache_layer.update_priorities(&mut self.nodes);
        }
    }

    pub fn update(&mut self, camera: Point3<f32>) {
        self.update_priorities(camera);

        // Update tile cache.
        self.breadth_first(|qt, id| {
            if qt.nodes[id].priority < Priority::cutoff() {
                return false;
            }

            for layer in 0..NUM_LAYERS {
                if qt.nodes[id].tile_indices[layer].is_some() &&
                    !qt.tile_cache_layers[layer].contains(id)
                {
                    qt.tile_cache_layers[layer].add_missing((qt.nodes[id].priority, id));
                }
            }
            true
        });
        for cache_layer in self.tile_cache_layers.iter_mut() {
            cache_layer.load_missing(&mut self.nodes);
        }

        // Determine visible nodes.
        self.visible_nodes.clear();
        for node in self.nodes.iter_mut() {
            node.visible = false;
        }
        // Any node with all needed layers in cache is visible...
        self.breadth_first(|qt, id| {
            qt.nodes[id].visible = qt.nodes[id].tile_indices.iter().filter_map(|i| *i).all(
                |i| {
                    qt.tile_cache_layers[i as usize].contains(id)
                },
            );
            qt.nodes[id].visible
        });
        // ...Except if all its children are visible instead.
        self.breadth_first(|qt, id| if qt.nodes[id].visible {
            qt.nodes[id].visible = !qt.nodes[id].children.iter().all(|child| match *child {
                Some(c) => qt.nodes[c].visible,
                None => false,
            });
            if qt.nodes[id].visible {
                qt.visible_nodes.push(id);
            }
            true
        } else {
            false
        });
    }

    fn breadth_first<F>(&mut self, mut visit: F)
    where
        F: FnMut(&mut Self, NodeId) -> bool,
    {
        let mut pending = VecDeque::new();
        if visit(self, NodeId::root()) {
            pending.push_back(NodeId::root());
        }
        while let Some(id) = pending.pop_front() {
            for i in 0..4 {
                if let Some(child) = self.nodes[id].children[i] {
                    if visit(self, child) {
                        pending.push_back(child);
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate ctest;
extern crate cc;

use std::env;
use std::str;

fn main() {
    let mut cfg = ctest::TestGenerator::new();

    let mut build = cc::Build::new();
    build.file("version_detect.c");
    if let Ok(out) = env::var("DEP_CURL_INCLUDE") {
        cfg.include(&out);
        build.include(&out);
    }
    let version = build.expand();
    let version = str::from_utf8(&version).unwrap();
    let version = version.lines()
        .filter(|l| !l.is_empty() && !l.starts_with("#"))
        .next()
        .and_then(|s| s.parse::<u32>().ok())
        .unwrap_or(10000);

    if env::var("TARGET").unwrap().contains("msvc") {
        cfg.flag("/wd4574"); // did you mean to use '#if INCL_WINSOCK_API_TYPEDEFS'
    }

    cfg.header("curl/curl.h");
    cfg.define("CURL_STATICLIB", None);
    cfg.field_name(|s, field| {
        if s == "curl_fileinfo" {
            field.replace("strings_", "strings.")
        } else if s == "CURLMsg" && field == "data" {
            "data.whatever".to_string()
        } else {
            field.to_string()
        }
    });
    cfg.type_name(|s, is_struct, _is_union| {
        match s {
            "CURL" |
            "CURLM" |
            "CURLSH" |
            "curl_version_info_data" => s.to_string(),
            "curl_khtype" |
            "curl_khstat" |
            "curl_khmatch" => format!("enum {}", s),
            s if is_struct => format!("struct {}", s),
            "sockaddr" => format!("struct sockaddr"),
            s => s.to_string(),
        }
    });
    // cfg.fn_cname(|s, l| l.unwrap_or(s).to_string());
    cfg.skip_type(|n| n == "__enum_ty");
    cfg.skip_signededness(|s| {
        s.ends_with("callback") || s.ends_with("function")
    });

    cfg.skip_struct(move |s| {
        if version < 60 {
            match s {
                "curl_version_info_data" => return true,
                _ => {}
            }
        }

        false
    });

    cfg.skip_const(move |s| {
        if version < 61 {
            match s {
                "CURLOPT_PIPEWAIT" => return true,
                _ => {}
            }
        }
        if version < 60 {
            match s {
                "CURLVERSION_FIFTH" |
                "CURLVERSION_NOW" => return true,
                _ => {}
            }
        }

        if version < 49 {
            if s.starts_with("CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE") {
                return true
            }
        }

        if version < 47 {
            if s.starts_with("CURL_HTTP_VERSION_2") {
                return true
            }
        }

        if version < 43 {
            if s.starts_with("CURLPIPE_") {
                return true
            }
        }

        // OSX doesn't have this yet
        s == "CURLSSLOPT_NO_REVOKE" ||

        // A lot of curl versions doesn't support unix sockets
        s == "CURLOPT_UNIX_SOCKET_PATH" || s == "CURL_VERSION_UNIX_SOCKETS"
    });

    if cfg!(target_env = "msvc") {
        cfg.skip_fn_ptrcheck(|s| s.starts_with("curl_"));
    }

    cfg.generate("../curl-sys/lib.rs", "all.rs");
}
<commit_msg>Skip CURLE_PEER_FAILED_VERIFICATION on older libcurl<commit_after>extern crate ctest;
extern crate cc;

use std::env;
use std::str;

fn main() {
    let mut cfg = ctest::TestGenerator::new();

    let mut build = cc::Build::new();
    build.file("version_detect.c");
    if let Ok(out) = env::var("DEP_CURL_INCLUDE") {
        cfg.include(&out);
        build.include(&out);
    }
    let version = build.expand();
    let version = str::from_utf8(&version).unwrap();
    let version = version.lines()
        .filter(|l| !l.is_empty() && !l.starts_with("#"))
        .next()
        .and_then(|s| s.parse::<u32>().ok())
        .unwrap_or(10000);

    if env::var("TARGET").unwrap().contains("msvc") {
        cfg.flag("/wd4574"); // did you mean to use '#if INCL_WINSOCK_API_TYPEDEFS'
    }

    cfg.header("curl/curl.h");
    cfg.define("CURL_STATICLIB", None);
    cfg.field_name(|s, field| {
        if s == "curl_fileinfo" {
            field.replace("strings_", "strings.")
        } else if s == "CURLMsg" && field == "data" {
            "data.whatever".to_string()
        } else {
            field.to_string()
        }
    });
    cfg.type_name(|s, is_struct, _is_union| {
        match s {
            "CURL" |
            "CURLM" |
            "CURLSH" |
            "curl_version_info_data" => s.to_string(),
            "curl_khtype" |
            "curl_khstat" |
            "curl_khmatch" => format!("enum {}", s),
            s if is_struct => format!("struct {}", s),
            "sockaddr" => format!("struct sockaddr"),
            s => s.to_string(),
        }
    });
    // cfg.fn_cname(|s, l| l.unwrap_or(s).to_string());
    cfg.skip_type(|n| n == "__enum_ty");
    cfg.skip_signededness(|s| {
        s.ends_with("callback") || s.ends_with("function")
    });

    cfg.skip_struct(move |s| {
        if version < 60 {
            match s {
                "curl_version_info_data" => return true,
                _ => {}
            }
        }

        false
    });

    cfg.skip_const(move |s| {
        if version < 61 {
            match s {
                "CURLOPT_PIPEWAIT" => return true,
                "CURLE_PEER_FAILED_VERIFICATION" => return true,
                _ => {}
            }
        }
        if version < 60 {
            match s {
                "CURLVERSION_FIFTH" |
                "CURLVERSION_NOW" => return true,
                _ => {}
            }
        }

        if version < 49 {
            if s.starts_with("CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE") {
                return true
            }
        }

        if version < 47 {
            if s.starts_with("CURL_HTTP_VERSION_2") {
                return true
            }
        }

        if version < 43 {
            if s.starts_with("CURLPIPE_") {
                return true
            }
        }

        // OSX doesn't have this yet
        s == "CURLSSLOPT_NO_REVOKE" ||

        // A lot of curl versions doesn't support unix sockets
        s == "CURLOPT_UNIX_SOCKET_PATH" || s == "CURL_VERSION_UNIX_SOCKETS"
    });

    if cfg!(target_env = "msvc") {
        cfg.skip_fn_ptrcheck(|s| s.starts_with("curl_"));
    }

    cfg.generate("../curl-sys/lib.rs", "all.rs");
}
<|endoftext|>


<commit_before>use std::{
    cell::Cell,
    os::unix::{io::RawFd, prelude::AsRawFd},
    ptr,
    sync::{Once, RwLock},
};

use io_lifetimes::OwnedFd;
use nix::{
    libc,
    sys::{
        mman,
        signal::{self, SigAction, SigHandler, Signal},
    },
};
use slog::{debug, trace};

thread_local!(static SIGBUS_GUARD: Cell<(*const MemMap, bool)> = Cell::new((ptr::null_mut(), false)));

static SIGBUS_INIT: Once = Once::new();
static mut OLD_SIGBUS_HANDLER: *mut SigAction = 0 as *mut SigAction;

#[derive(Debug)]
pub struct Pool {
    // TODO: We may need to change this to a Mutex in order to support protocols like wlr screencopy which may
    // require we write to a memmap.
    map: RwLock<MemMap>,
    fd: OwnedFd,
    log: ::slog::Logger,
}

// SAFETY: The memmap is owned by the pool and content is only accessible via a reference.
unsafe impl Send for Pool {}
// SAFETY: The memmap is guarded by a RwLock, meaning no writers may mutate the memmap when it is being read.
unsafe impl Sync for Pool {}

pub enum ResizeError {
    InvalidSize,
    MremapFailed,
}

impl Pool {
    pub fn new(fd: OwnedFd, size: usize, log: ::slog::Logger) -> Result<Pool, OwnedFd> {
        let memmap = match MemMap::new(fd.as_raw_fd(), size) {
            Ok(memmap) => memmap,
            Err(_) => {
                return Err(fd);
            }
        };
        trace!(log, "Creating new shm pool"; "fd" => fd.as_raw_fd() as i32, "size" => size);
        Ok(Pool {
            map: RwLock::new(memmap),
            fd,
            log,
        })
    }

    pub fn resize(&self, newsize: i32) -> Result<(), ResizeError> {
        let mut guard = self.map.write().unwrap();
        let oldsize = guard.size();

        if newsize <= 0 || oldsize > (newsize as usize) {
            return Err(ResizeError::InvalidSize);
        }

        trace!(self.log, "Resizing shm pool"; "fd" => self.fd.as_raw_fd() as i32, "oldsize" => oldsize, "newsize" => newsize);

        guard.remap(newsize as usize).map_err(|()| {
            debug!(self.log, "SHM pool resize failed"; "fd" => self.fd.as_raw_fd() as i32, "oldsize" => oldsize, "newsize" => newsize);
            ResizeError::MremapFailed
        })
    }

    pub fn size(&self) -> usize {
        self.map.read().unwrap().size
    }

    pub fn with_data_slice<T, F: FnOnce(&[u8]) -> T>(&self, f: F) -> Result<T, ()> {
        // Place the sigbus handler
        SIGBUS_INIT.call_once(|| unsafe {
            place_sigbus_handler();
        });

        let pool_guard = self.map.read().unwrap();

        trace!(self.log, "Buffer access on shm pool"; "fd" => self.fd.as_raw_fd() as i32);

        // Prepare the access
        SIGBUS_GUARD.with(|guard| {
            let (p, _) = guard.get();
            if !p.is_null() {
                // Recursive call of this method is not supported
                panic!("Recursive access to a SHM pool content is not supported.");
            }
            guard.set((&*pool_guard as *const MemMap, false))
        });

        let slice = pool_guard.get_slice();
        let t = f(slice);

        // Cleanup Post-access
        SIGBUS_GUARD.with(|guard| {
            let (_, triggered) = guard.get();
            guard.set((ptr::null_mut(), false));
            if triggered {
                debug!(self.log, "SIGBUS caught on access on shm pool"; "fd" => self.fd.as_raw_fd());
                Err(())
            } else {
                Ok(t)
            }
        })
    }
}

#[derive(Debug)]
struct MemMap {
    ptr: *mut u8,
    fd: RawFd,
    size: usize,
}

impl MemMap {
    fn new(fd: RawFd, size: usize) -> Result<MemMap, ()> {
        Ok(MemMap {
            ptr: unsafe { map(fd, size) }?,
            fd,
            size,
        })
    }

    fn remap(&mut self, newsize: usize) -> Result<(), ()> {
        if self.ptr.is_null() {
            return Err(());
        }
        // memunmap cannot fail, as we are unmapping a pre-existing map
        let _ = unsafe { unmap(self.ptr, self.size) };
        // remap the fd with the new size
        match unsafe { map(self.fd, newsize) } {
            Ok(ptr) => {
                // update the parameters
                self.ptr = ptr;
                self.size = newsize;
                Ok(())
            }
            Err(()) => {
                // set ourselves in an empty state
                self.ptr = ptr::null_mut();
                self.size = 0;
                self.fd = -1;
                Err(())
            }
        }
    }

    fn size(&self) -> usize {
        self.size
    }

    fn get_slice(&self) -> &[u8] {
        // if we are in the 'invalid state', self.size == 0 and we return &[]
        // which is perfectly safe even if self.ptr is null
        unsafe { ::std::slice::from_raw_parts(self.ptr, self.size) }
    }

    fn contains(&self, ptr: *mut u8) -> bool {
        ptr >= self.ptr && ptr < unsafe { self.ptr.add(self.size) }
    }

    fn nullify(&self) -> Result<(), ()> {
        unsafe { nullify_map(self.ptr, self.size) }
    }
}

impl Drop for MemMap {
    fn drop(&mut self) {
        if !self.ptr.is_null() {
            let _ = unsafe { unmap(self.ptr, self.size) };
        }
    }
}

unsafe fn map(fd: RawFd, size: usize) -> Result<*mut u8, ()> {
    let ret = mman::mmap(
        ptr::null_mut(),
        size,
        mman::ProtFlags::PROT_READ,
        mman::MapFlags::MAP_SHARED,
        fd,
        0,
    );
    ret.map(|p| p as *mut u8).map_err(|_| ())
}

unsafe fn unmap(ptr: *mut u8, size: usize) -> Result<(), ()> {
    let ret = mman::munmap(ptr as *mut _, size);
    ret.map_err(|_| ())
}

unsafe fn nullify_map(ptr: *mut u8, size: usize) -> Result<(), ()> {
    let ret = mman::mmap(
        ptr as *mut _,
        size,
        mman::ProtFlags::PROT_READ,
        mman::MapFlags::MAP_ANONYMOUS | mman::MapFlags::MAP_PRIVATE | mman::MapFlags::MAP_FIXED,
        -1,
        0,
    );
    ret.map(|_| ()).map_err(|_| ())
}

unsafe fn place_sigbus_handler() {
    // create our sigbus handler
    let action = SigAction::new(
        SigHandler::SigAction(sigbus_handler),
        signal::SaFlags::SA_NODEFER,
        signal::SigSet::empty(),
    );
    match signal::sigaction(Signal::SIGBUS, &action) {
        Ok(old_signal) => {
            OLD_SIGBUS_HANDLER = Box::into_raw(Box::new(old_signal));
        }
        Err(e) => panic!("sigaction failed sor SIGBUS handler: {:?}", e),
    }
}

unsafe fn reraise_sigbus() {
    // reset the old sigaction
    let _ = signal::sigaction(Signal::SIGBUS, &*OLD_SIGBUS_HANDLER);
    let _ = signal::raise(Signal::SIGBUS);
}

extern "C" fn sigbus_handler(_signum: libc::c_int, info: *mut libc::siginfo_t, _context: *mut libc::c_void) {
    let faulty_ptr = unsafe { siginfo_si_addr(info) } as *mut u8;
    SIGBUS_GUARD.with(|guard| {
        let (memmap, _) = guard.get();
        match unsafe { memmap.as_ref() }.map(|m| (m, m.contains(faulty_ptr))) {
            Some((m, true)) => {
                // we are in a faulty memory pool !
                // remember that it was faulty
                guard.set((memmap, true));
                // nullify the pool
                if m.nullify().is_err() {
                    // something terrible occurred !
                    unsafe { reraise_sigbus() }
                }
            }
            _ => {
                // something else occurred, let's die honorably
                unsafe { reraise_sigbus() }
            }
        }
    });
}

// This was shamelessly stolen from rustc's source
// so I expect it to work whenever rust works
// I guess it's good enough?

#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
    #[repr(C)]
    struct siginfo_t {
        a: [libc::c_int; 3], // si_signo, si_errno, si_code
        si_addr: *mut libc::c_void,
    }

    (*(info as *const siginfo_t)).si_addr
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
    (*info).si_addr
}
<commit_msg>wayland/shm: Fix unsafe access to dangling buffer<commit_after>use std::{
    cell::Cell,
    os::unix::{io::RawFd, prelude::AsRawFd},
    ptr,
    sync::{Once, RwLock},
};

use io_lifetimes::OwnedFd;
use nix::{
    libc,
    sys::{
        mman,
        signal::{self, SigAction, SigHandler, Signal},
    },
};
use slog::{debug, trace};

thread_local!(static SIGBUS_GUARD: Cell<(*const MemMap, bool)> = Cell::new((ptr::null_mut(), false)));

static SIGBUS_INIT: Once = Once::new();
static mut OLD_SIGBUS_HANDLER: *mut SigAction = 0 as *mut SigAction;

#[derive(Debug)]
pub struct Pool {
    // TODO: We may need to change this to a Mutex in order to support protocols like wlr screencopy which may
    // require we write to a memmap.
    map: RwLock<MemMap>,
    fd: OwnedFd,
    log: ::slog::Logger,
}

// SAFETY: The memmap is owned by the pool and content is only accessible via a reference.
unsafe impl Send for Pool {}
// SAFETY: The memmap is guarded by a RwLock, meaning no writers may mutate the memmap when it is being read.
unsafe impl Sync for Pool {}

pub enum ResizeError {
    InvalidSize,
    MremapFailed,
}

impl Pool {
    pub fn new(fd: OwnedFd, size: usize, log: ::slog::Logger) -> Result<Pool, OwnedFd> {
        let memmap = match MemMap::new(fd.as_raw_fd(), size) {
            Ok(memmap) => memmap,
            Err(_) => {
                return Err(fd);
            }
        };
        trace!(log, "Creating new shm pool"; "fd" => fd.as_raw_fd() as i32, "size" => size);
        Ok(Pool {
            map: RwLock::new(memmap),
            fd,
            log,
        })
    }

    pub fn resize(&self, newsize: i32) -> Result<(), ResizeError> {
        let mut guard = self.map.write().unwrap();
        let oldsize = guard.size();

        if newsize <= 0 || oldsize > (newsize as usize) {
            return Err(ResizeError::InvalidSize);
        }

        trace!(self.log, "Resizing shm pool"; "fd" => self.fd.as_raw_fd() as i32, "oldsize" => oldsize, "newsize" => newsize);

        guard.remap(newsize as usize).map_err(|()| {
            debug!(self.log, "SHM pool resize failed"; "fd" => self.fd.as_raw_fd() as i32, "oldsize" => oldsize, "newsize" => newsize);
            ResizeError::MremapFailed
        })
    }

    pub fn size(&self) -> usize {
        self.map.read().unwrap().size
    }

    pub fn with_data_slice<T, F: FnOnce(&[u8]) -> T>(&self, f: F) -> Result<T, ()> {
        // Place the sigbus handler
        SIGBUS_INIT.call_once(|| unsafe {
            place_sigbus_handler();
        });

        let pool_guard = self.map.read().unwrap();

        trace!(self.log, "Buffer access on shm pool"; "fd" => self.fd.as_raw_fd() as i32);

        // Prepare the access
        SIGBUS_GUARD.with(|guard| {
            let (p, _) = guard.get();
            if !p.is_null() {
                // Recursive call of this method is not supported
                panic!("Recursive access to a SHM pool content is not supported.");
            }
            guard.set((&*pool_guard as *const MemMap, false))
        });

        let slice = pool_guard.get_slice();
        let t = f(slice);

        // Cleanup Post-access
        SIGBUS_GUARD.with(|guard| {
            let (_, triggered) = guard.get();
            guard.set((ptr::null_mut(), false));
            if triggered {
                debug!(self.log, "SIGBUS caught on access on shm pool"; "fd" => self.fd.as_raw_fd());
                Err(())
            } else {
                Ok(t)
            }
        })
    }
}

#[derive(Debug)]
struct MemMap {
    ptr: *mut u8,
    fd: RawFd,
    size: usize,
}

impl MemMap {
    fn new(fd: RawFd, size: usize) -> Result<MemMap, ()> {
        Ok(MemMap {
            ptr: unsafe { map(fd, size) }?,
            fd,
            size,
        })
    }

    fn remap(&mut self, newsize: usize) -> Result<(), ()> {
        if self.ptr.is_null() {
            return Err(());
        }
        // memunmap cannot fail, as we are unmapping a pre-existing map
        let _ = unsafe { unmap(self.ptr, self.size) };
        // remap the fd with the new size
        match unsafe { map(self.fd, newsize) } {
            Ok(ptr) => {
                // update the parameters
                self.ptr = ptr;
                self.size = newsize;
                Ok(())
            }
            Err(()) => {
                // set ourselves in an empty state
                self.ptr = ptr::null_mut();
                self.size = 0;
                self.fd = -1;
                Err(())
            }
        }
    }

    fn size(&self) -> usize {
        self.size
    }

    fn get_slice(&self) -> &[u8] {
        if self.ptr.is_null() {
            &[]
        } else {
            // SAFETY: if we are in the 'invalid state', `self.ptr` is null and the previous branch is used
            unsafe { ::std::slice::from_raw_parts(self.ptr, self.size) }
        }
    }

    fn contains(&self, ptr: *mut u8) -> bool {
        ptr >= self.ptr && ptr < unsafe { self.ptr.add(self.size) }
    }

    fn nullify(&self) -> Result<(), ()> {
        unsafe { nullify_map(self.ptr, self.size) }
    }
}

impl Drop for MemMap {
    fn drop(&mut self) {
        if !self.ptr.is_null() {
            let _ = unsafe { unmap(self.ptr, self.size) };
        }
    }
}

unsafe fn map(fd: RawFd, size: usize) -> Result<*mut u8, ()> {
    let ret = mman::mmap(
        ptr::null_mut(),
        size,
        mman::ProtFlags::PROT_READ,
        mman::MapFlags::MAP_SHARED,
        fd,
        0,
    );
    ret.map(|p| p as *mut u8).map_err(|_| ())
}

unsafe fn unmap(ptr: *mut u8, size: usize) -> Result<(), ()> {
    let ret = mman::munmap(ptr as *mut _, size);
    ret.map_err(|_| ())
}

unsafe fn nullify_map(ptr: *mut u8, size: usize) -> Result<(), ()> {
    let ret = mman::mmap(
        ptr as *mut _,
        size,
        mman::ProtFlags::PROT_READ,
        mman::MapFlags::MAP_ANONYMOUS | mman::MapFlags::MAP_PRIVATE | mman::MapFlags::MAP_FIXED,
        -1,
        0,
    );
    ret.map(|_| ()).map_err(|_| ())
}

unsafe fn place_sigbus_handler() {
    // create our sigbus handler
    let action = SigAction::new(
        SigHandler::SigAction(sigbus_handler),
        signal::SaFlags::SA_NODEFER,
        signal::SigSet::empty(),
    );
    match signal::sigaction(Signal::SIGBUS, &action) {
        Ok(old_signal) => {
            OLD_SIGBUS_HANDLER = Box::into_raw(Box::new(old_signal));
        }
        Err(e) => panic!("sigaction failed sor SIGBUS handler: {:?}", e),
    }
}

unsafe fn reraise_sigbus() {
    // reset the old sigaction
    let _ = signal::sigaction(Signal::SIGBUS, &*OLD_SIGBUS_HANDLER);
    let _ = signal::raise(Signal::SIGBUS);
}

extern "C" fn sigbus_handler(_signum: libc::c_int, info: *mut libc::siginfo_t, _context: *mut libc::c_void) {
    let faulty_ptr = unsafe { siginfo_si_addr(info) } as *mut u8;
    SIGBUS_GUARD.with(|guard| {
        let (memmap, _) = guard.get();
        match unsafe { memmap.as_ref() }.map(|m| (m, m.contains(faulty_ptr))) {
            Some((m, true)) => {
                // we are in a faulty memory pool !
                // remember that it was faulty
                guard.set((memmap, true));
                // nullify the pool
                if m.nullify().is_err() {
                    // something terrible occurred !
                    unsafe { reraise_sigbus() }
                }
            }
            _ => {
                // something else occurred, let's die honorably
                unsafe { reraise_sigbus() }
            }
        }
    });
}

// This was shamelessly stolen from rustc's source
// so I expect it to work whenever rust works
// I guess it's good enough?

#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
    #[repr(C)]
    struct siginfo_t {
        a: [libc::c_int; 3], // si_signo, si_errno, si_code
        si_addr: *mut libc::c_void,
    }

    (*(info as *const siginfo_t)).si_addr
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> *mut libc::c_void {
    (*info).si_addr
}
<|endoftext|>