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

// Type resolution: the phase that finds all the types in the AST with
// unresolved type variables and replaces "ty_var" types with their
// substitutions.

use middle::pat_util;
use middle::ty;
use middle::typeck::check::{FnCtxt, SelfInfo};
use middle::typeck::infer::{force_all, resolve_all, resolve_region};
use middle::typeck::infer::resolve_type;
use middle::typeck::infer;
use middle::typeck::{vtable_res, vtable_origin};
use middle::typeck::{vtable_static, vtable_param};
use middle::typeck::method_map_entry;
use middle::typeck::write_substs_to_tcx;
use middle::typeck::write_ty_to_tcx;
use util::ppaux;

use syntax::ast;
use syntax::codemap::Span;
use syntax::print::pprust::pat_to_str;
use syntax::oldvisit;

fn resolve_type_vars_in_type(fcx: @mut FnCtxt, sp: Span, typ: ty::t)
                          -> Option<ty::t> {
    if !ty::type_needs_infer(typ) { return Some(typ); }
    match resolve_type(fcx.infcx(), typ, resolve_all | force_all) {
        Ok(new_type) => return Some(new_type),
        Err(e) => {
            if !fcx.ccx.tcx.sess.has_errors() {
                fcx.ccx.tcx.sess.span_err(
                    sp,
                    fmt!("cannot determine a type \
                          for this expression: %s",
                         infer::fixup_err_to_str(e)))
            }
            return None;
        }
    }
}

fn resolve_type_vars_in_types(fcx: @mut FnCtxt, sp: Span, tys: &[ty::t])
                          -> ~[ty::t] {
    tys.map(|t| {
        match resolve_type_vars_in_type(fcx, sp, *t) {
            Some(t1) => t1,
            None => ty::mk_err()
        }
    })
}

fn resolve_method_map_entry(fcx: @mut FnCtxt, sp: Span, id: ast::NodeId) {
    // Resolve any method map entry
    match fcx.inh.method_map.find(&id) {
        None => {}
        Some(mme) => {
            {
                let r = resolve_type_vars_in_type(fcx, sp, mme.self_ty);
                for t in r.iter() {
                    let method_map = fcx.ccx.method_map;
                    let new_entry = method_map_entry { self_ty: *t, ..*mme };
                    debug!("writeback::resolve_method_map_entry(id=%?, \
                            new_entry=%?)",
                           id, new_entry);
                    method_map.insert(id, new_entry);
                }
            }
        }
    }
}

fn resolve_vtable_map_entry(fcx: @mut FnCtxt, sp: Span, id: ast::NodeId) {
    // Resolve any method map entry
    match fcx.inh.vtable_map.find(&id) {
        None => {}
        Some(origins) => {
            let r_origins = resolve_origins(fcx, sp, *origins);
            let vtable_map = fcx.ccx.vtable_map;
            vtable_map.insert(id, r_origins);
            debug!("writeback::resolve_vtable_map_entry(id=%d, vtables=%?)",
                   id, r_origins.repr(fcx.tcx()));
        }
    }

    fn resolve_origins(fcx: @mut FnCtxt, sp: Span,
                       vtbls: vtable_res) -> vtable_res {
        @vtbls.map(|os| @os.map(|o| resolve_origin(fcx, sp, o)))
    }

    fn resolve_origin(fcx: @mut FnCtxt,
                      sp: Span,
                      origin: &vtable_origin) -> vtable_origin {
        match origin {
            &vtable_static(def_id, ref tys, origins) => {
                let r_tys = resolve_type_vars_in_types(fcx, sp, *tys);
                let r_origins = resolve_origins(fcx, sp, origins);
                vtable_static(def_id, r_tys, r_origins)
            }
            &vtable_param(n, b) => {
                vtable_param(n, b)
            }
        }
    }
}

fn resolve_type_vars_for_node(wbcx: @mut WbCtxt, sp: Span, id: ast::NodeId)
                           -> Option<ty::t> {
    let fcx = wbcx.fcx;
    let tcx = fcx.ccx.tcx;

    // Resolve any borrowings for the node with id `id`
    match fcx.inh.adjustments.find(&id) {
        None => (),

        Some(&@ty::AutoAddEnv(r, s)) => {
            match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
                Err(e) => {
                    // This should not, I think, happen:
                    fcx.ccx.tcx.sess.span_err(
                        sp, fmt!("cannot resolve bound for closure: %s",
                                 infer::fixup_err_to_str(e)));
                }
                Ok(r1) => {
                    let resolved_adj = @ty::AutoAddEnv(r1, s);
                    debug!("Adjustments for node %d: %?", id, resolved_adj);
                    fcx.tcx().adjustments.insert(id, resolved_adj);
                }
            }
        }

        Some(&@ty::AutoDerefRef(adj)) => {
            let fixup_region = |r| {
                match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
                    Ok(r1) => r1,
                    Err(e) => {
                        // This should not, I think, happen.
                        fcx.ccx.tcx.sess.span_err(
                            sp, fmt!("cannot resolve scope of borrow: %s",
                                     infer::fixup_err_to_str(e)));
                        r
                    }
                }
            };

            let resolved_autoref = match adj.autoref {
                None => None,
                Some(ref r) => Some(r.map_region(fixup_region))
            };

            let resolved_adj = @ty::AutoDerefRef(ty::AutoDerefRef {
                autoderefs: adj.autoderefs,
                autoref: resolved_autoref,
            });
            debug!("Adjustments for node %d: %?", id, resolved_adj);
            fcx.tcx().adjustments.insert(id, resolved_adj);
        }
    }

    // Resolve the type of the node with id `id`
    let n_ty = fcx.node_ty(id);
    match resolve_type_vars_in_type(fcx, sp, n_ty) {
      None => {
        wbcx.success = false;
        return None;
      }

      Some(t) => {
        debug!("resolve_type_vars_for_node(id=%d, n_ty=%s, t=%s)",
               id, ppaux::ty_to_str(tcx, n_ty), ppaux::ty_to_str(tcx, t));
        write_ty_to_tcx(tcx, id, t);
        let mut ret = Some(t);
        do fcx.opt_node_ty_substs(id) |substs| {
          let mut new_tps = ~[];
          for subst in substs.tps.iter() {
              match resolve_type_vars_in_type(fcx, sp, *subst) {
                Some(t) => new_tps.push(t),
                None => { wbcx.success = false; ret = None; break }
              }
          }
          write_substs_to_tcx(tcx, id, new_tps);
          ret.is_some()
        };
        ret
      }
    }
}

fn maybe_resolve_type_vars_for_node(wbcx: @mut WbCtxt,
                                    sp: Span,
                                    id: ast::NodeId)
                                 -> Option<ty::t> {
    if wbcx.fcx.inh.node_types.contains_key(&id) {
        resolve_type_vars_for_node(wbcx, sp, id)
    } else {
        None
    }
}

struct WbCtxt {
    fcx: @mut FnCtxt,

    // As soon as we hit an error we have to stop resolving
    // the entire function.
    success: bool,
}

type wb_vt = oldvisit::vt<@mut WbCtxt>;

fn visit_stmt(s: @ast::Stmt, (wbcx, v): (@mut WbCtxt, wb_vt)) {
    if !wbcx.success { return; }
    resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s));
    oldvisit::visit_stmt(s, (wbcx, v));
}

fn visit_expr(e: @ast::Expr, (wbcx, v): (@mut WbCtxt, wb_vt)) {
    if !wbcx.success {
        return;
    }

    resolve_type_vars_for_node(wbcx, e.span, e.id);

    resolve_method_map_entry(wbcx.fcx, e.span, e.id);
    {
        let r = e.get_callee_id();
        for callee_id in r.iter() {
            resolve_method_map_entry(wbcx.fcx, e.span, *callee_id);
        }
    }

    resolve_vtable_map_entry(wbcx.fcx, e.span, e.id);
    {
        let r = e.get_callee_id();
        for callee_id in r.iter() {
            resolve_vtable_map_entry(wbcx.fcx, e.span, *callee_id);
        }
    }

    match e.node {
        ast::ExprFnBlock(ref decl, _) => {
            for input in decl.inputs.iter() {
                let _ = resolve_type_vars_for_node(wbcx, e.span, input.id);
            }
        }

        ast::ExprBinary(callee_id, _, _, _) |
        ast::ExprUnary(callee_id, _, _) |
        ast::ExprAssignOp(callee_id, _, _, _) |
        ast::ExprIndex(callee_id, _, _) => {
            maybe_resolve_type_vars_for_node(wbcx, e.span, callee_id);
        }

        ast::ExprMethodCall(callee_id, _, _, _, _, _) => {
            // We must always have written in a callee ID type for these.
            resolve_type_vars_for_node(wbcx, e.span, callee_id);
        }

        _ => ()
    }

    oldvisit::visit_expr(e, (wbcx, v));
}

fn visit_block(b: &ast::Block, (wbcx, v): (@mut WbCtxt, wb_vt)) {
    if !wbcx.success {
        return;
    }

    resolve_type_vars_for_node(wbcx, b.span, b.id);
    oldvisit::visit_block(b, (wbcx, v));
}

fn visit_pat(p: @ast::Pat, (wbcx, v): (@mut WbCtxt, wb_vt)) {
    if !wbcx.success {
        return;
    }

    resolve_type_vars_for_node(wbcx, p.span, p.id);
    debug!("Type for pattern binding %s (id %d) resolved to %s",
           pat_to_str(p, wbcx.fcx.ccx.tcx.sess.intr()), p.id,
           wbcx.fcx.infcx().ty_to_str(
               ty::node_id_to_type(wbcx.fcx.ccx.tcx,
                                   p.id)));
    oldvisit::visit_pat(p, (wbcx, v));
}

fn visit_local(l: @ast::Local, (wbcx, v): (@mut WbCtxt, wb_vt)) {
    if !wbcx.success { return; }
    let var_ty = wbcx.fcx.local_ty(l.span, l.id);
    match resolve_type(wbcx.fcx.infcx(), var_ty, resolve_all | force_all) {
        Ok(lty) => {
            debug!("Type for local %s (id %d) resolved to %s",
                   pat_to_str(l.pat, wbcx.fcx.tcx().sess.intr()),
                   l.id,
                   wbcx.fcx.infcx().ty_to_str(lty));
            write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.id, lty);
        }
        Err(e) => {
            wbcx.fcx.ccx.tcx.sess.span_err(
                l.span,
                fmt!("cannot determine a type \
                      for this local variable: %s",
                     infer::fixup_err_to_str(e)));
            wbcx.success = false;
        }
    }
    oldvisit::visit_local(l, (wbcx, v));
}
fn visit_item(_item: @ast::item, (_wbcx, _v): (@mut WbCtxt, wb_vt)) {
    // Ignore items
}

fn mk_visitor() -> oldvisit::vt<@mut WbCtxt> {
    oldvisit::mk_vt(@oldvisit::Visitor {visit_item: visit_item,
                                  visit_stmt: visit_stmt,
                                  visit_expr: visit_expr,
                                  visit_block: visit_block,
                                  visit_pat: visit_pat,
                                  visit_local: visit_local,
                                  .. *oldvisit::default_visitor()})
}

pub fn resolve_type_vars_in_expr(fcx: @mut FnCtxt, e: @ast::Expr) -> bool {
    let wbcx = @mut WbCtxt { fcx: fcx, success: true };
    let visit = mk_visitor();
    (visit.visit_expr)(e, (wbcx, visit));
    return wbcx.success;
}

pub fn resolve_type_vars_in_fn(fcx: @mut FnCtxt,
                               decl: &ast::fn_decl,
                               blk: &ast::Block,
                               self_info: Option<SelfInfo>) -> bool {
    let wbcx = @mut WbCtxt { fcx: fcx, success: true };
    let visit = mk_visitor();
    (visit.visit_block)(blk, (wbcx, visit));
    for self_info in self_info.iter() {
        resolve_type_vars_for_node(wbcx,
                                   self_info.span,
                                   self_info.self_id);
    }
    for arg in decl.inputs.iter() {
        (visit.visit_pat)(arg.pat, (wbcx, visit));
        // Privacy needs the type for the whole pattern, not just each binding
        if !pat_util::pat_is_binding(fcx.tcx().def_map, arg.pat) {
            resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id);
        }
    }
    return wbcx.success;
}
<commit_msg>librustc: Port writeback to the new API.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Type resolution: the phase that finds all the types in the AST with
// unresolved type variables and replaces "ty_var" types with their
// substitutions.

use middle::pat_util;
use middle::ty;
use middle::typeck::check::{FnCtxt, SelfInfo};
use middle::typeck::infer::{force_all, resolve_all, resolve_region};
use middle::typeck::infer::resolve_type;
use middle::typeck::infer;
use middle::typeck::{vtable_res, vtable_origin};
use middle::typeck::{vtable_static, vtable_param};
use middle::typeck::method_map_entry;
use middle::typeck::write_substs_to_tcx;
use middle::typeck::write_ty_to_tcx;
use util::ppaux;

use syntax::ast;
use syntax::codemap::Span;
use syntax::print::pprust::pat_to_str;
use syntax::visit;
use syntax::visit::Visitor;

fn resolve_type_vars_in_type(fcx: @mut FnCtxt, sp: Span, typ: ty::t)
                          -> Option<ty::t> {
    if !ty::type_needs_infer(typ) { return Some(typ); }
    match resolve_type(fcx.infcx(), typ, resolve_all | force_all) {
        Ok(new_type) => return Some(new_type),
        Err(e) => {
            if !fcx.ccx.tcx.sess.has_errors() {
                fcx.ccx.tcx.sess.span_err(
                    sp,
                    fmt!("cannot determine a type \
                          for this expression: %s",
                         infer::fixup_err_to_str(e)))
            }
            return None;
        }
    }
}

fn resolve_type_vars_in_types(fcx: @mut FnCtxt, sp: Span, tys: &[ty::t])
                          -> ~[ty::t] {
    tys.map(|t| {
        match resolve_type_vars_in_type(fcx, sp, *t) {
            Some(t1) => t1,
            None => ty::mk_err()
        }
    })
}

fn resolve_method_map_entry(fcx: @mut FnCtxt, sp: Span, id: ast::NodeId) {
    // Resolve any method map entry
    match fcx.inh.method_map.find(&id) {
        None => {}
        Some(mme) => {
            {
                let r = resolve_type_vars_in_type(fcx, sp, mme.self_ty);
                for t in r.iter() {
                    let method_map = fcx.ccx.method_map;
                    let new_entry = method_map_entry { self_ty: *t, ..*mme };
                    debug!("writeback::resolve_method_map_entry(id=%?, \
                            new_entry=%?)",
                           id, new_entry);
                    method_map.insert(id, new_entry);
                }
            }
        }
    }
}

fn resolve_vtable_map_entry(fcx: @mut FnCtxt, sp: Span, id: ast::NodeId) {
    // Resolve any method map entry
    match fcx.inh.vtable_map.find(&id) {
        None => {}
        Some(origins) => {
            let r_origins = resolve_origins(fcx, sp, *origins);
            let vtable_map = fcx.ccx.vtable_map;
            vtable_map.insert(id, r_origins);
            debug!("writeback::resolve_vtable_map_entry(id=%d, vtables=%?)",
                   id, r_origins.repr(fcx.tcx()));
        }
    }

    fn resolve_origins(fcx: @mut FnCtxt, sp: Span,
                       vtbls: vtable_res) -> vtable_res {
        @vtbls.map(|os| @os.map(|o| resolve_origin(fcx, sp, o)))
    }

    fn resolve_origin(fcx: @mut FnCtxt,
                      sp: Span,
                      origin: &vtable_origin) -> vtable_origin {
        match origin {
            &vtable_static(def_id, ref tys, origins) => {
                let r_tys = resolve_type_vars_in_types(fcx, sp, *tys);
                let r_origins = resolve_origins(fcx, sp, origins);
                vtable_static(def_id, r_tys, r_origins)
            }
            &vtable_param(n, b) => {
                vtable_param(n, b)
            }
        }
    }
}

fn resolve_type_vars_for_node(wbcx: @mut WbCtxt, sp: Span, id: ast::NodeId)
                           -> Option<ty::t> {
    let fcx = wbcx.fcx;
    let tcx = fcx.ccx.tcx;

    // Resolve any borrowings for the node with id `id`
    match fcx.inh.adjustments.find(&id) {
        None => (),

        Some(&@ty::AutoAddEnv(r, s)) => {
            match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
                Err(e) => {
                    // This should not, I think, happen:
                    fcx.ccx.tcx.sess.span_err(
                        sp, fmt!("cannot resolve bound for closure: %s",
                                 infer::fixup_err_to_str(e)));
                }
                Ok(r1) => {
                    let resolved_adj = @ty::AutoAddEnv(r1, s);
                    debug!("Adjustments for node %d: %?", id, resolved_adj);
                    fcx.tcx().adjustments.insert(id, resolved_adj);
                }
            }
        }

        Some(&@ty::AutoDerefRef(adj)) => {
            let fixup_region = |r| {
                match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
                    Ok(r1) => r1,
                    Err(e) => {
                        // This should not, I think, happen.
                        fcx.ccx.tcx.sess.span_err(
                            sp, fmt!("cannot resolve scope of borrow: %s",
                                     infer::fixup_err_to_str(e)));
                        r
                    }
                }
            };

            let resolved_autoref = match adj.autoref {
                None => None,
                Some(ref r) => Some(r.map_region(fixup_region))
            };

            let resolved_adj = @ty::AutoDerefRef(ty::AutoDerefRef {
                autoderefs: adj.autoderefs,
                autoref: resolved_autoref,
            });
            debug!("Adjustments for node %d: %?", id, resolved_adj);
            fcx.tcx().adjustments.insert(id, resolved_adj);
        }
    }

    // Resolve the type of the node with id `id`
    let n_ty = fcx.node_ty(id);
    match resolve_type_vars_in_type(fcx, sp, n_ty) {
      None => {
        wbcx.success = false;
        return None;
      }

      Some(t) => {
        debug!("resolve_type_vars_for_node(id=%d, n_ty=%s, t=%s)",
               id, ppaux::ty_to_str(tcx, n_ty), ppaux::ty_to_str(tcx, t));
        write_ty_to_tcx(tcx, id, t);
        let mut ret = Some(t);
        do fcx.opt_node_ty_substs(id) |substs| {
          let mut new_tps = ~[];
          for subst in substs.tps.iter() {
              match resolve_type_vars_in_type(fcx, sp, *subst) {
                Some(t) => new_tps.push(t),
                None => { wbcx.success = false; ret = None; break }
              }
          }
          write_substs_to_tcx(tcx, id, new_tps);
          ret.is_some()
        };
        ret
      }
    }
}

fn maybe_resolve_type_vars_for_node(wbcx: @mut WbCtxt,
                                    sp: Span,
                                    id: ast::NodeId)
                                 -> Option<ty::t> {
    if wbcx.fcx.inh.node_types.contains_key(&id) {
        resolve_type_vars_for_node(wbcx, sp, id)
    } else {
        None
    }
}

struct WbCtxt {
    fcx: @mut FnCtxt,

    // As soon as we hit an error we have to stop resolving
    // the entire function.
    success: bool,
}

struct WbVisitor;

fn visit_stmt(s: @ast::Stmt, (wbcx, v): (@mut WbCtxt, &mut WbVisitor)) {
    if !wbcx.success { return; }
    resolve_type_vars_for_node(wbcx, s.span, ty::stmt_node_id(s));
    visit::walk_stmt(v, s, wbcx);
}

fn visit_expr(e: @ast::Expr, (wbcx, v): (@mut WbCtxt, &mut WbVisitor)) {
    if !wbcx.success {
        return;
    }

    resolve_type_vars_for_node(wbcx, e.span, e.id);

    resolve_method_map_entry(wbcx.fcx, e.span, e.id);
    {
        let r = e.get_callee_id();
        for callee_id in r.iter() {
            resolve_method_map_entry(wbcx.fcx, e.span, *callee_id);
        }
    }

    resolve_vtable_map_entry(wbcx.fcx, e.span, e.id);
    {
        let r = e.get_callee_id();
        for callee_id in r.iter() {
            resolve_vtable_map_entry(wbcx.fcx, e.span, *callee_id);
        }
    }

    match e.node {
        ast::ExprFnBlock(ref decl, _) => {
            for input in decl.inputs.iter() {
                let _ = resolve_type_vars_for_node(wbcx, e.span, input.id);
            }
        }

        ast::ExprBinary(callee_id, _, _, _) |
        ast::ExprUnary(callee_id, _, _) |
        ast::ExprAssignOp(callee_id, _, _, _) |
        ast::ExprIndex(callee_id, _, _) => {
            maybe_resolve_type_vars_for_node(wbcx, e.span, callee_id);
        }

        ast::ExprMethodCall(callee_id, _, _, _, _, _) => {
            // We must always have written in a callee ID type for these.
            resolve_type_vars_for_node(wbcx, e.span, callee_id);
        }

        _ => ()
    }

    visit::walk_expr(v, e, wbcx);
}

fn visit_block(b: &ast::Block, (wbcx, v): (@mut WbCtxt, &mut WbVisitor)) {
    if !wbcx.success {
        return;
    }

    resolve_type_vars_for_node(wbcx, b.span, b.id);
    visit::walk_block(v, b, wbcx);
}

fn visit_pat(p: @ast::Pat, (wbcx, v): (@mut WbCtxt, &mut WbVisitor)) {
    if !wbcx.success {
        return;
    }

    resolve_type_vars_for_node(wbcx, p.span, p.id);
    debug!("Type for pattern binding %s (id %d) resolved to %s",
           pat_to_str(p, wbcx.fcx.ccx.tcx.sess.intr()), p.id,
           wbcx.fcx.infcx().ty_to_str(
               ty::node_id_to_type(wbcx.fcx.ccx.tcx,
                                   p.id)));
    visit::walk_pat(v, p, wbcx);
}

fn visit_local(l: @ast::Local, (wbcx, v): (@mut WbCtxt, &mut WbVisitor)) {
    if !wbcx.success { return; }
    let var_ty = wbcx.fcx.local_ty(l.span, l.id);
    match resolve_type(wbcx.fcx.infcx(), var_ty, resolve_all | force_all) {
        Ok(lty) => {
            debug!("Type for local %s (id %d) resolved to %s",
                   pat_to_str(l.pat, wbcx.fcx.tcx().sess.intr()),
                   l.id,
                   wbcx.fcx.infcx().ty_to_str(lty));
            write_ty_to_tcx(wbcx.fcx.ccx.tcx, l.id, lty);
        }
        Err(e) => {
            wbcx.fcx.ccx.tcx.sess.span_err(
                l.span,
                fmt!("cannot determine a type \
                      for this local variable: %s",
                     infer::fixup_err_to_str(e)));
            wbcx.success = false;
        }
    }
    visit::walk_local(v, l, wbcx);
}
fn visit_item(_item: @ast::item, (_wbcx, _v): (@mut WbCtxt, &mut WbVisitor)) {
    // Ignore items
}

impl Visitor<@mut WbCtxt> for WbVisitor {
    fn visit_item(&mut self, i:@ast::item, e:@mut WbCtxt) { visit_item(i, (e, self)); }
    fn visit_stmt(&mut self, s:@ast::Stmt, e:@mut WbCtxt) { visit_stmt(s, (e, self)); }
    fn visit_expr(&mut self, ex:@ast::Expr, e:@mut WbCtxt) { visit_expr(ex, (e, self)); }
    fn visit_block(&mut self, b:&ast::Block, e:@mut WbCtxt) { visit_block(b, (e, self)); }
    fn visit_pat(&mut self, p:@ast::Pat, e:@mut WbCtxt) { visit_pat(p, (e, self)); }
    fn visit_local(&mut self, l:@ast::Local, e:@mut WbCtxt) { visit_local(l, (e, self)); }
}

pub fn resolve_type_vars_in_expr(fcx: @mut FnCtxt, e: @ast::Expr) -> bool {
    let wbcx = @mut WbCtxt { fcx: fcx, success: true };
    let mut visit = WbVisitor;
    visit.visit_expr(e, wbcx);
    return wbcx.success;
}

pub fn resolve_type_vars_in_fn(fcx: @mut FnCtxt,
                               decl: &ast::fn_decl,
                               blk: &ast::Block,
                               self_info: Option<SelfInfo>) -> bool {
    let wbcx = @mut WbCtxt { fcx: fcx, success: true };
    let mut visit = WbVisitor;
    visit.visit_block(blk, wbcx);
    for self_info in self_info.iter() {
        resolve_type_vars_for_node(wbcx,
                                   self_info.span,
                                   self_info.self_id);
    }
    for arg in decl.inputs.iter() {
        visit.visit_pat(arg.pat, wbcx);
        // Privacy needs the type for the whole pattern, not just each binding
        if !pat_util::pat_is_binding(fcx.tcx().def_map, arg.pat) {
            resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id);
        }
    }
    return wbcx.success;
}
<|endoftext|>


<commit_before>extern crate rmp_serde;
extern crate time;

use std::cmp;
use std::collections::{HashMap, HashSet};
use std::ops::Add;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use self::time::Duration;
use super::{
    MerkleTree,
    RemoteNode,
};
use super::rust_base58::{FromBase58, ToBase58};
use super::types::*;

pub const CATCHUP_ROUND_TIMEOUT: i64 = 120;

enum CatchupStepResult {
    Finished,
    Continue,
    FailedAtNode(usize),
}

enum CatchupProgress {
    ShouldBeStarted,
    NotNeeded,
    Finished(MerkleTree),
    InProgress,
}

pub struct CatchupHandler {
    pub f: usize,
    pub ledger_status_same: usize,
    pub merkle_tree: MerkleTree,
    pub target_mt_size: usize,
    pub target_mt_root: Vec<u8>,
    pub new_mt_vote: usize,
    pub nodes: Vec<RemoteNode>,
    pub initiate_cmd_id: i32,
    pub is_refresh: bool,
    pub pending_catchup: Option<CatchUpProcess>,
    pub timeout: time::Tm,
    pub pool_id: i32,
    pub nodes_votes: Vec<Option<(String, usize)>>,
}

impl Default for CatchupHandler {
    fn default() -> Self {
        CatchupHandler {
            f: 0,
            ledger_status_same: 0,
            merkle_tree: MerkleTree::from_vec(Vec::new()).unwrap(),
            nodes: Vec::new(),
            target_mt_size: 0,
            new_mt_vote: 0,
            target_mt_root: Vec::new(),
            pending_catchup: None,
            initiate_cmd_id: 0,
            is_refresh: false,
            pool_id: 0,
            nodes_votes: Vec::new(),
            timeout: time::now_utc(),
        }
    }
}

impl CatchupHandler {
    pub fn process_msg(&mut self, msg: Message, raw_msg: &String, src_ind: usize) -> Result<Option<MerkleTree>, PoolError> {
        let catchup_status: CatchupProgress = match msg {
            Message::Pong => {
                //sending ledger status
                //TODO not send ledger status directly as response on ping, wait pongs from all nodes?
                let ls: LedgerStatus = LedgerStatus {
                    txnSeqNo: self.nodes.len(),
                    merkleRoot: self.merkle_tree.root_hash().as_slice().to_base58(),
                    ledgerId: 0,
                    ppSeqNo: None,
                    viewNo: None,
                };
                let resp_msg: Message = Message::LedgerStatus(ls);
                self.nodes[src_ind].send_msg(&resp_msg)?;
                CatchupProgress::InProgress
            }
            Message::LedgerStatus(ledger_status) => {
                self.nodes_votes[src_ind] = Some((ledger_status.merkleRoot, ledger_status.txnSeqNo));
                self.check_nodes_responses_on_status()?
            }
            Message::ConsistencyProof(cons_proof) => {
                self.nodes_votes[src_ind] = Some((cons_proof.newMerkleRoot, cons_proof.seqNoEnd));
                self.check_nodes_responses_on_status()?
            }
            Message::CatchupRep(catchup) => {
                self.process_catchup_rep(catchup, src_ind)?
            }
            _ => {
                warn!("unhandled msg {:?}", msg);
                CatchupProgress::InProgress
            }
        };

        match catchup_status {
            CatchupProgress::Finished(mt) => return Ok(Some(mt)),
            CatchupProgress::NotNeeded => return Ok(Some(self.merkle_tree.clone())),
            CatchupProgress::ShouldBeStarted => self.start_catchup()?,
            CatchupProgress::InProgress => { /* nothing to do */ }
        }
        Ok(None)
    }

    fn check_nodes_responses_on_status(&mut self) -> Result<CatchupProgress, PoolError> {
        if self.pending_catchup.is_some() {
            return Ok(CatchupProgress::InProgress);
        }
        let mut votes: HashMap<(String, usize), usize> = HashMap::new();
        for node_vote in &self.nodes_votes {
            if let &Some(ref node_vote) = node_vote {
                let cnt = *votes.get(&node_vote).unwrap_or(&0) + 1;
                votes.insert((node_vote.0.clone(), node_vote.1), cnt);
            }
        }
        if let Some((most_popular_vote, votes_cnt)) = votes.iter().max_by_key(|entry| entry.1) {
            if *votes_cnt == self.nodes.len() - self.f {
                let &(ref target_mt_root, target_mt_size) = most_popular_vote;
                let cur_mt_size = self.merkle_tree.count();
                let cur_mt_hash = self.merkle_tree.root_hash().to_base58();
                if target_mt_size == cur_mt_size {
                    if cur_mt_hash.eq(target_mt_root) {
                        return Ok(CatchupProgress::NotNeeded);
                    } else {
                        return Err(PoolError::CommonError(CommonError::InvalidState(
                            "Ledger merkle tree doesn't acceptable for current tree.".to_string())));
                    }
                } else if target_mt_size > cur_mt_size {
                    self.target_mt_size = target_mt_size;
                    self.target_mt_root = target_mt_root.from_base58().map_err(|_|
                        CommonError::InvalidStructure(
                            "Can't parse target MerkleTree hash from nodes responses".to_string()))?;
                    return Ok(CatchupProgress::ShouldBeStarted);
                } else {
                    return Err(PoolError::CommonError(CommonError::InvalidState(
                        "Local merkle tree greater than mt from ledger".to_string())));
                }
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    pub fn reset_nodes_votes(&mut self) {
        self.nodes_votes.clear();
        self.nodes_votes.resize(self.nodes.len(), None);
    }

    pub fn start_catchup(&mut self) -> Result<(), PoolError> {
        trace!("start_catchup");
        if self.pending_catchup.is_some() {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "CatchUp already started for the pool".to_string())));
        }

        let active_node_cnt = self.nodes.iter().filter(|node| !node.is_blacklisted).count();

        if active_node_cnt == 0 { // TODO FIXME
            return Err(PoolError::Terminate);
        }

        let txns_cnt_in_cur_mt = self.merkle_tree.count();
        let cnt_to_catchup = self.target_mt_size - txns_cnt_in_cur_mt;
        if cnt_to_catchup <= 0 {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Nothing to CatchUp, but started".to_string())));
        }

        self.pending_catchup = Some(CatchUpProcess {
            merkle_tree: self.merkle_tree.clone(),
            pending_reps: Vec::new(),
            resp_not_received_node_idx: HashSet::new(),
        });
        self.timeout = time::now_utc().add(Duration::seconds(CATCHUP_ROUND_TIMEOUT));

        let portion = (cnt_to_catchup + active_node_cnt - 1) / active_node_cnt; //TODO check standard round up div
        let mut catchup_req = CatchupReq {
            ledgerId: 0,
            seqNoStart: txns_cnt_in_cur_mt + 1,
            seqNoEnd: txns_cnt_in_cur_mt + 1 + portion - 1,
            catchupTill: self.target_mt_size,
        };
        for idx in 0..self.nodes.len() {
            let node = &self.nodes[idx];
            //TODO do not perform duplicate requests, update resp_not_received_node_idx
            if node.is_blacklisted {
                continue;
            }
            self.pending_catchup.as_mut().map(|pc| {
                pc.resp_not_received_node_idx.insert(idx);
            });
            node.send_msg(&Message::CatchupReq(catchup_req.clone()))?;
            catchup_req.seqNoStart += portion;
            catchup_req.seqNoEnd = cmp::min(catchup_req.seqNoStart + portion - 1,
                                            catchup_req.catchupTill);

            if catchup_req.seqNoStart > catchup_req.seqNoEnd {
                // We don't have more portions to ask
                break;
            }
        }
        Ok(())
    }

    fn process_catchup_rep(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupProgress, PoolError> {
        trace!("append {:?}", catchup);
        let catchup_finished = self.catchup_step(catchup, node_idx)?;
        match catchup_finished {
            CatchupStepResult::Finished => return Ok(CatchupProgress::Finished(self.finish_catchup()?)),
            CatchupStepResult::Continue => { /* nothing to do */ }
            CatchupStepResult::FailedAtNode(failed_node_idx) => {
                warn!("Fail to continue catch-up by response from node with idx {}. Node will be blacklisted and catchup will be restarted", failed_node_idx);
                self.nodes[failed_node_idx].is_blacklisted = true;
                self.pending_catchup = None;
                // TODO may be send ledger status again and re-obtain target MerkleTree params
                self.start_catchup()?
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn catchup_step(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupStepResult, PoolError> {
        let process = self.pending_catchup.as_mut()
            .ok_or(CommonError::InvalidState("Process non-existing CatchUp".to_string()))?;
        process.pending_reps.push((catchup, node_idx));
        process.resp_not_received_node_idx.remove(&node_idx);

        while !process.pending_reps.is_empty() {
            let index = process.pending_reps.get_min_index()?;
            {
                let &mut (ref mut first_resp, node_idx) = process.pending_reps.get_mut(index)
                    .ok_or(CommonError::InvalidStructure(format!("Element not Found")))?;
                if first_resp.min_tx()? - 1 != process.merkle_tree.count() { break; }

                let mut temp_mt = process.merkle_tree.clone();
                while !first_resp.txns.is_empty() {
                    let key = first_resp.min_tx()?.to_string();
                    let new_gen_tx = first_resp.txns.remove(&key).unwrap();
                    if let Ok(new_get_txn_bytes) = rmp_serde::to_vec_named(&new_gen_tx) {
                        temp_mt.append(new_get_txn_bytes)?;
                    } else {
                        return Ok(CatchupStepResult::FailedAtNode(node_idx));
                    }
                }

                if CatchupHandler::check_cons_proofs(&temp_mt, &first_resp.consProof, &self.target_mt_root, self.target_mt_size)
                    .map_err(map_err_err!()).is_err() {
                    return Ok(CatchupStepResult::FailedAtNode(node_idx));
                }

                process.merkle_tree = temp_mt;
            }
            process.pending_reps.remove(index);
        }
        trace!("updated mt hash {}, tree {:?}", process.merkle_tree.root_hash().as_slice().to_base58(), process.merkle_tree);
        if &process.merkle_tree.count() == &self.target_mt_size {
            if process.merkle_tree.root_hash().ne(&self.target_mt_root) {
                return Err(PoolError::CommonError(CommonError::InvalidState(
                    "CatchUp failed: all transactions added, proofs checked, but root hash differ with target".to_string())));
            }
            return Ok(CatchupStepResult::Finished);
        } else {
            return Ok(CatchupStepResult::Continue);
        }
    }

    fn check_cons_proofs(mt: &MerkleTree, cons_proofs: &Vec<String>, target_mt_root: &Vec<u8>, target_mt_size: usize) -> Result<(), CommonError> {
        let mut bytes_proofs: Vec<Vec<u8>> = Vec::new();
        for cons_proof in cons_proofs {
            let cons_proof: &String = cons_proof;
            bytes_proofs.push(cons_proof.from_base58().map_err(|err|
                CommonError::InvalidStructure(
                    format!("Can't decode node consistency proof: {}", err)))?)
        }
        if !mt.consistency_proof(target_mt_root, target_mt_size, &bytes_proofs)? {
            return Err(CommonError::InvalidStructure("Consistency proof verification failed".to_string()));
        }
        Ok(())
    }

    fn finish_catchup(&mut self) -> Result<MerkleTree, PoolError> {
        Ok(self.pending_catchup.take().
            ok_or(CommonError::InvalidState("Try to finish non-existing CatchUp".to_string()))?
            .merkle_tree)
    }

    pub fn flush_requests(&mut self, status: Result<(), PoolError>) -> Result<(), PoolError> {
        if self.initiate_cmd_id == -1 {
            return Ok(());
        }
        let cmd = if self.is_refresh {
            PoolCommand::RefreshAck(self.initiate_cmd_id, status)
        } else {
            PoolCommand::OpenAck(self.initiate_cmd_id, self.pool_id, status)
        };
        self.initiate_cmd_id = -1;
        CommandExecutor::instance()
            .send(Command::Pool(cmd))
            .map_err(|err|
                PoolError::CommonError(
                    CommonError::InvalidState("Can't send ACK cmd".to_string())))
    }

    pub fn get_upcoming_timeout(&self) -> Option<time::Tm> {
        Some(self.timeout)
    }

    pub fn process_timeout(&mut self) -> Result<(), PoolError> {
        let pc = if let Some(pc) = self.pending_catchup.take() { pc } else {
            return Err(PoolError::Timeout);
        };
        warn!("Fail to continue catch-up response(s) not received from nodes with idx {:?}. Node will be blacklisted and catchup will be restarted", pc.resp_not_received_node_idx);
        pc.resp_not_received_node_idx.iter()
            .for_each(|idx| self.nodes[*idx].is_blacklisted = true);
        // TODO may be send ledger status again and re-obtain target MerkleTree params
        self.start_catchup()
    }
}
<commit_msg>Revert catchup round timeout.<commit_after>extern crate rmp_serde;
extern crate time;

use std::cmp;
use std::collections::{HashMap, HashSet};
use std::ops::Add;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use self::time::Duration;
use super::{
    MerkleTree,
    RemoteNode,
};
use super::rust_base58::{FromBase58, ToBase58};
use super::types::*;

pub const CATCHUP_ROUND_TIMEOUT: i64 = 50;

enum CatchupStepResult {
    Finished,
    Continue,
    FailedAtNode(usize),
}

enum CatchupProgress {
    ShouldBeStarted,
    NotNeeded,
    Finished(MerkleTree),
    InProgress,
}

pub struct CatchupHandler {
    pub f: usize,
    pub ledger_status_same: usize,
    pub merkle_tree: MerkleTree,
    pub target_mt_size: usize,
    pub target_mt_root: Vec<u8>,
    pub new_mt_vote: usize,
    pub nodes: Vec<RemoteNode>,
    pub initiate_cmd_id: i32,
    pub is_refresh: bool,
    pub pending_catchup: Option<CatchUpProcess>,
    pub timeout: time::Tm,
    pub pool_id: i32,
    pub nodes_votes: Vec<Option<(String, usize)>>,
}

impl Default for CatchupHandler {
    fn default() -> Self {
        CatchupHandler {
            f: 0,
            ledger_status_same: 0,
            merkle_tree: MerkleTree::from_vec(Vec::new()).unwrap(),
            nodes: Vec::new(),
            target_mt_size: 0,
            new_mt_vote: 0,
            target_mt_root: Vec::new(),
            pending_catchup: None,
            initiate_cmd_id: 0,
            is_refresh: false,
            pool_id: 0,
            nodes_votes: Vec::new(),
            timeout: time::now_utc(),
        }
    }
}

impl CatchupHandler {
    pub fn process_msg(&mut self, msg: Message, raw_msg: &String, src_ind: usize) -> Result<Option<MerkleTree>, PoolError> {
        let catchup_status: CatchupProgress = match msg {
            Message::Pong => {
                //sending ledger status
                //TODO not send ledger status directly as response on ping, wait pongs from all nodes?
                let ls: LedgerStatus = LedgerStatus {
                    txnSeqNo: self.nodes.len(),
                    merkleRoot: self.merkle_tree.root_hash().as_slice().to_base58(),
                    ledgerId: 0,
                    ppSeqNo: None,
                    viewNo: None,
                };
                let resp_msg: Message = Message::LedgerStatus(ls);
                self.nodes[src_ind].send_msg(&resp_msg)?;
                CatchupProgress::InProgress
            }
            Message::LedgerStatus(ledger_status) => {
                self.nodes_votes[src_ind] = Some((ledger_status.merkleRoot, ledger_status.txnSeqNo));
                self.check_nodes_responses_on_status()?
            }
            Message::ConsistencyProof(cons_proof) => {
                self.nodes_votes[src_ind] = Some((cons_proof.newMerkleRoot, cons_proof.seqNoEnd));
                self.check_nodes_responses_on_status()?
            }
            Message::CatchupRep(catchup) => {
                self.process_catchup_rep(catchup, src_ind)?
            }
            _ => {
                warn!("unhandled msg {:?}", msg);
                CatchupProgress::InProgress
            }
        };

        match catchup_status {
            CatchupProgress::Finished(mt) => return Ok(Some(mt)),
            CatchupProgress::NotNeeded => return Ok(Some(self.merkle_tree.clone())),
            CatchupProgress::ShouldBeStarted => self.start_catchup()?,
            CatchupProgress::InProgress => { /* nothing to do */ }
        }
        Ok(None)
    }

    fn check_nodes_responses_on_status(&mut self) -> Result<CatchupProgress, PoolError> {
        if self.pending_catchup.is_some() {
            return Ok(CatchupProgress::InProgress);
        }
        let mut votes: HashMap<(String, usize), usize> = HashMap::new();
        for node_vote in &self.nodes_votes {
            if let &Some(ref node_vote) = node_vote {
                let cnt = *votes.get(&node_vote).unwrap_or(&0) + 1;
                votes.insert((node_vote.0.clone(), node_vote.1), cnt);
            }
        }
        if let Some((most_popular_vote, votes_cnt)) = votes.iter().max_by_key(|entry| entry.1) {
            if *votes_cnt == self.nodes.len() - self.f {
                let &(ref target_mt_root, target_mt_size) = most_popular_vote;
                let cur_mt_size = self.merkle_tree.count();
                let cur_mt_hash = self.merkle_tree.root_hash().to_base58();
                if target_mt_size == cur_mt_size {
                    if cur_mt_hash.eq(target_mt_root) {
                        return Ok(CatchupProgress::NotNeeded);
                    } else {
                        return Err(PoolError::CommonError(CommonError::InvalidState(
                            "Ledger merkle tree doesn't acceptable for current tree.".to_string())));
                    }
                } else if target_mt_size > cur_mt_size {
                    self.target_mt_size = target_mt_size;
                    self.target_mt_root = target_mt_root.from_base58().map_err(|_|
                        CommonError::InvalidStructure(
                            "Can't parse target MerkleTree hash from nodes responses".to_string()))?;
                    return Ok(CatchupProgress::ShouldBeStarted);
                } else {
                    return Err(PoolError::CommonError(CommonError::InvalidState(
                        "Local merkle tree greater than mt from ledger".to_string())));
                }
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    pub fn reset_nodes_votes(&mut self) {
        self.nodes_votes.clear();
        self.nodes_votes.resize(self.nodes.len(), None);
    }

    pub fn start_catchup(&mut self) -> Result<(), PoolError> {
        trace!("start_catchup");
        if self.pending_catchup.is_some() {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "CatchUp already started for the pool".to_string())));
        }

        let active_node_cnt = self.nodes.iter().filter(|node| !node.is_blacklisted).count();

        if active_node_cnt == 0 { // TODO FIXME
            return Err(PoolError::Terminate);
        }

        let txns_cnt_in_cur_mt = self.merkle_tree.count();
        let cnt_to_catchup = self.target_mt_size - txns_cnt_in_cur_mt;
        if cnt_to_catchup <= 0 {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Nothing to CatchUp, but started".to_string())));
        }

        self.pending_catchup = Some(CatchUpProcess {
            merkle_tree: self.merkle_tree.clone(),
            pending_reps: Vec::new(),
            resp_not_received_node_idx: HashSet::new(),
        });
        self.timeout = time::now_utc().add(Duration::seconds(CATCHUP_ROUND_TIMEOUT));

        let portion = (cnt_to_catchup + active_node_cnt - 1) / active_node_cnt; //TODO check standard round up div
        let mut catchup_req = CatchupReq {
            ledgerId: 0,
            seqNoStart: txns_cnt_in_cur_mt + 1,
            seqNoEnd: txns_cnt_in_cur_mt + 1 + portion - 1,
            catchupTill: self.target_mt_size,
        };
        for idx in 0..self.nodes.len() {
            let node = &self.nodes[idx];
            //TODO do not perform duplicate requests, update resp_not_received_node_idx
            if node.is_blacklisted {
                continue;
            }
            self.pending_catchup.as_mut().map(|pc| {
                pc.resp_not_received_node_idx.insert(idx);
            });
            node.send_msg(&Message::CatchupReq(catchup_req.clone()))?;
            catchup_req.seqNoStart += portion;
            catchup_req.seqNoEnd = cmp::min(catchup_req.seqNoStart + portion - 1,
                                            catchup_req.catchupTill);

            if catchup_req.seqNoStart > catchup_req.seqNoEnd {
                // We don't have more portions to ask
                break;
            }
        }
        Ok(())
    }

    fn process_catchup_rep(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupProgress, PoolError> {
        trace!("append {:?}", catchup);
        let catchup_finished = self.catchup_step(catchup, node_idx)?;
        match catchup_finished {
            CatchupStepResult::Finished => return Ok(CatchupProgress::Finished(self.finish_catchup()?)),
            CatchupStepResult::Continue => { /* nothing to do */ }
            CatchupStepResult::FailedAtNode(failed_node_idx) => {
                warn!("Fail to continue catch-up by response from node with idx {}. Node will be blacklisted and catchup will be restarted", failed_node_idx);
                self.nodes[failed_node_idx].is_blacklisted = true;
                self.pending_catchup = None;
                // TODO may be send ledger status again and re-obtain target MerkleTree params
                self.start_catchup()?
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn catchup_step(&mut self, catchup: CatchupRep, node_idx: usize) -> Result<CatchupStepResult, PoolError> {
        let process = self.pending_catchup.as_mut()
            .ok_or(CommonError::InvalidState("Process non-existing CatchUp".to_string()))?;
        process.pending_reps.push((catchup, node_idx));
        process.resp_not_received_node_idx.remove(&node_idx);

        while !process.pending_reps.is_empty() {
            let index = process.pending_reps.get_min_index()?;
            {
                let &mut (ref mut first_resp, node_idx) = process.pending_reps.get_mut(index)
                    .ok_or(CommonError::InvalidStructure(format!("Element not Found")))?;
                if first_resp.min_tx()? - 1 != process.merkle_tree.count() { break; }

                let mut temp_mt = process.merkle_tree.clone();
                while !first_resp.txns.is_empty() {
                    let key = first_resp.min_tx()?.to_string();
                    let new_gen_tx = first_resp.txns.remove(&key).unwrap();
                    if let Ok(new_get_txn_bytes) = rmp_serde::to_vec_named(&new_gen_tx) {
                        temp_mt.append(new_get_txn_bytes)?;
                    } else {
                        return Ok(CatchupStepResult::FailedAtNode(node_idx));
                    }
                }

                if CatchupHandler::check_cons_proofs(&temp_mt, &first_resp.consProof, &self.target_mt_root, self.target_mt_size)
                    .map_err(map_err_err!()).is_err() {
                    return Ok(CatchupStepResult::FailedAtNode(node_idx));
                }

                process.merkle_tree = temp_mt;
            }
            process.pending_reps.remove(index);
        }
        trace!("updated mt hash {}, tree {:?}", process.merkle_tree.root_hash().as_slice().to_base58(), process.merkle_tree);
        if &process.merkle_tree.count() == &self.target_mt_size {
            if process.merkle_tree.root_hash().ne(&self.target_mt_root) {
                return Err(PoolError::CommonError(CommonError::InvalidState(
                    "CatchUp failed: all transactions added, proofs checked, but root hash differ with target".to_string())));
            }
            return Ok(CatchupStepResult::Finished);
        } else {
            return Ok(CatchupStepResult::Continue);
        }
    }

    fn check_cons_proofs(mt: &MerkleTree, cons_proofs: &Vec<String>, target_mt_root: &Vec<u8>, target_mt_size: usize) -> Result<(), CommonError> {
        let mut bytes_proofs: Vec<Vec<u8>> = Vec::new();
        for cons_proof in cons_proofs {
            let cons_proof: &String = cons_proof;
            bytes_proofs.push(cons_proof.from_base58().map_err(|err|
                CommonError::InvalidStructure(
                    format!("Can't decode node consistency proof: {}", err)))?)
        }
        if !mt.consistency_proof(target_mt_root, target_mt_size, &bytes_proofs)? {
            return Err(CommonError::InvalidStructure("Consistency proof verification failed".to_string()));
        }
        Ok(())
    }

    fn finish_catchup(&mut self) -> Result<MerkleTree, PoolError> {
        Ok(self.pending_catchup.take().
            ok_or(CommonError::InvalidState("Try to finish non-existing CatchUp".to_string()))?
            .merkle_tree)
    }

    pub fn flush_requests(&mut self, status: Result<(), PoolError>) -> Result<(), PoolError> {
        if self.initiate_cmd_id == -1 {
            return Ok(());
        }
        let cmd = if self.is_refresh {
            PoolCommand::RefreshAck(self.initiate_cmd_id, status)
        } else {
            PoolCommand::OpenAck(self.initiate_cmd_id, self.pool_id, status)
        };
        self.initiate_cmd_id = -1;
        CommandExecutor::instance()
            .send(Command::Pool(cmd))
            .map_err(|err|
                PoolError::CommonError(
                    CommonError::InvalidState("Can't send ACK cmd".to_string())))
    }

    pub fn get_upcoming_timeout(&self) -> Option<time::Tm> {
        Some(self.timeout)
    }

    pub fn process_timeout(&mut self) -> Result<(), PoolError> {
        let pc = if let Some(pc) = self.pending_catchup.take() { pc } else {
            return Err(PoolError::Timeout);
        };
        warn!("Fail to continue catch-up response(s) not received from nodes with idx {:?}. Node will be blacklisted and catchup will be restarted", pc.resp_not_received_node_idx);
        pc.resp_not_received_node_idx.iter()
            .for_each(|idx| self.nodes[*idx].is_blacklisted = true);
        // TODO may be send ledger status again and re-obtain target MerkleTree params
        self.start_catchup()
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Rasterization powered by FreeType and FontConfig
use std::collections::HashMap;

use freetype::{self, Library, Face};

mod list_fonts;

use self::list_fonts::fc;
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style};

/// Rasterizes glyphs for a single font face.
pub struct FreeTypeRasterizer {
    faces: HashMap<FontKey, Face<'static>>,
    library: Library,
    keys: HashMap<FontDesc, FontKey>,
    dpi_x: u32,
    dpi_y: u32,
    dpr: f32,
}

#[inline]
fn to_freetype_26_6(f: f32) -> isize {
    ((1i32 << 6) as f32 * f) as isize
}

impl ::Rasterize for FreeTypeRasterizer {
    type Err = Error;

    fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32, _: bool) -> Result<FreeTypeRasterizer, Error> {
        let library = Library::init()?;

        Ok(FreeTypeRasterizer {
            faces: HashMap::new(),
            keys: HashMap::new(),
            library: library,
            dpi_x: dpi_x as u32,
            dpi_y: dpi_y as u32,
            dpr: device_pixel_ratio,
        })
    }

    fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
        let face = self.faces
            .get(&key)
            .ok_or(Error::FontNotLoaded)?;

        let scale_size = self.dpr as f64 * size.as_f32_pts() as f64;

        let em_size = face.em_size() as f64;
        let w = face.max_advance_width() as f64;
        let h = (face.ascender() - face.descender() + face.height()) as f64;

        let w_scale = w * scale_size / em_size;
        let h_scale = h * scale_size / em_size;

        Ok(Metrics {
            average_advance: w_scale,
            line_height: h_scale,
        })
    }

    fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
        self.keys
            .get(&desc.to_owned())
            .map(|k| Ok(*k))
            .unwrap_or_else(|| {
                let face = self.get_face(desc)?;
                let key = FontKey::next();
                self.faces.insert(key, face);
                Ok(key)
            })
    }

    fn get_glyph(&mut self, glyph_key: &GlyphKey) -> Result<RasterizedGlyph, Error> {
        self.get_rendered_glyph(glyph_key, false)
    }

}

pub trait IntoFontconfigType {
    type FcType;
    fn into_fontconfig_type(&self) -> Self::FcType;
}

impl IntoFontconfigType for Slant {
    type FcType = fc::Slant;
    fn into_fontconfig_type(&self) -> Self::FcType {
        match *self {
            Slant::Normal => fc::Slant::Roman,
            Slant::Italic => fc::Slant::Italic,
            Slant::Oblique => fc::Slant::Oblique,
        }
    }
}

impl IntoFontconfigType for Weight {
    type FcType = fc::Weight;

    fn into_fontconfig_type(&self) -> Self::FcType {
        match *self {
            Weight::Normal => fc::Weight::Regular,
            Weight::Bold => fc::Weight::Bold,
        }
    }
}

impl FreeTypeRasterizer {
    /// Load a font face accoring to `FontDesc`
    fn get_face(&mut self, desc: &FontDesc) -> Result<Face<'static>, Error> {
        match desc.style {
            Style::Description { slant, weight } => {
                // Match nearest font
                self.get_matching_face(&desc, slant, weight)
            }
            Style::Specific(ref style) => {
                // If a name was specified, try and load specifically that font.
                self.get_specific_face(&desc, &style)
            }
        }
    }

    fn get_matching_face(
        &mut self,
        desc: &FontDesc,
        slant: Slant,
        weight: Weight
    ) -> Result<Face<'static>, Error> {
        let mut pattern = fc::Pattern::new();
        pattern.add_family(&desc.name);
        pattern.set_weight(weight.into_fontconfig_type());
        pattern.set_slant(slant.into_fontconfig_type());

        let fonts = fc::font_sort(fc::Config::get_current(), &mut pattern)
            .ok_or_else(|| Error::MissingFont(desc.to_owned()))?;

        // Take first font that has a path
        for font in &fonts {
            if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
                return Ok(self.library.new_face(path, index)?);
            }
        }

        Err(Error::MissingFont(desc.to_owned()))
    }

    fn get_specific_face(
        &mut self,
        desc: &FontDesc,
        style: &str
    ) -> Result<Face<'static>, Error> {
        let mut pattern = fc::Pattern::new();
        pattern.add_family(&desc.name);
        pattern.add_style(style);

        let font = fc::font_match(fc::Config::get_current(), &mut pattern)
            .ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
        if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
            println!("got font path={:?}", path);
            return Ok(self.library.new_face(path, index)?);
        } else {
            Err(Error::MissingFont(desc.to_owned()))
        }
    }

    fn get_rendered_glyph(&mut self, glyph_key: &GlyphKey, have_recursed: bool)
                          -> Result<RasterizedGlyph, Error> {
        let faces = self.faces.clone();
        let face = faces
            .get(&glyph_key.font_key)
            .ok_or(Error::FontNotLoaded)?;

        let size = glyph_key.size.as_f32_pts() * self.dpr;
        let c = glyph_key.c;

        face.set_char_size(to_freetype_26_6(size), 0, self.dpi_x, self.dpi_y)?;
        let index = face.get_char_index(c as usize);

        if index == 0 && have_recursed == false {
            let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key);
            let new_glyph_key = GlyphKey {
                c: glyph_key.c,
                font_key: key,
                size: glyph_key.size
            };

            return self.get_rendered_glyph(&new_glyph_key, true);
        }

        face.load_glyph(index as u32, freetype::face::TARGET_LIGHT)?;
        let glyph = face.glyph();
        glyph.render_glyph(freetype::render_mode::RenderMode::Lcd)?;

        unsafe {
            let ft_lib = self.library.raw();
            freetype::ffi::FT_Library_SetLcdFilter(
                ft_lib,
                freetype::ffi::FT_LCD_FILTER_DEFAULT
            );
        }

        let bitmap = glyph.bitmap();
        let buf = bitmap.buffer();
        let pitch = bitmap.pitch() as usize;

        let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize);
        for i in 0..bitmap.rows() {
            let start = (i as usize) * pitch;
            let stop = start + bitmap.width() as usize;
            packed.extend_from_slice(&buf[start..stop]);
        }

        Ok(RasterizedGlyph {
            c: c,
            top: glyph.bitmap_top(),
            left: glyph.bitmap_left(),
            width: glyph.bitmap().width() / 3,
            height: glyph.bitmap().rows(),
            buf: packed,
        })
    }

    fn load_face_with_glyph(&mut self, glyph: char) -> Result<FontKey, Error> {
        let mut charset = fc::CharSet::new();
        charset.add(glyph);
        let mut pattern = fc::Pattern::new();
        unsafe {
            pattern.add_charset(&charset);
        }

        let config = fc::Config::get_current();
        match fc::font_match(config, &mut pattern) {
            Some(font) => {
                if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
                    let face = self.library.new_face(path, index)?;
                    let key = FontKey::next();
                    self.faces.insert(key, face);
                    return Ok(key)
                }
                Err(Error::MissingFont(
                    FontDesc::new("fallback-without-path", Style::Specific(glyph.to_string()))
                ))
            },
            None => {
                Err(Error::MissingFont(
                    FontDesc::new("no-fallback-for", Style::Specific(glyph.to_string()))
                ))
            }
        }
    }
}

/// Errors occurring when using the freetype rasterizer
#[derive(Debug)]
pub enum Error {
    /// Error occurred within the FreeType library
    FreeType(freetype::Error),

    /// Couldn't find font matching description
    MissingFont(FontDesc),

    /// Requested an operation with a FontKey that isn't known to the rasterizer
    FontNotLoaded,
}

impl ::std::error::Error for Error {
    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            Error::FreeType(ref err) => Some(err),
            _ => None,
        }
    }

    fn description(&self) -> &str {
        match *self {
            Error::FreeType(ref err) => err.description(),
            Error::MissingFont(ref _desc) => "couldn't find the requested font",
            Error::FontNotLoaded => "tried to operate on font that hasn't been loaded",
        }
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Error::FreeType(ref err) => {
                err.fmt(f)
            },
            Error::MissingFont(ref desc) => {
                write!(f, "Couldn't find a font with {}\
                       \n\tPlease check the font config in your alacritty.yml.", desc)
            },
            Error::FontNotLoaded => {
                f.write_str("Tried to use a font that hasn't been loaded")
            }
        }
    }
}

impl From<freetype::Error> for Error {
    fn from(val: freetype::Error) -> Error {
        Error::FreeType(val)
    }
}

unsafe impl Send for FreeTypeRasterizer {}
<commit_msg>Rework font cache to cache on paths<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Rasterization powered by FreeType and FontConfig
use std::collections::HashMap;

use freetype::{self, Library, Face};

mod list_fonts;

use self::list_fonts::fc;
use super::{FontDesc, RasterizedGlyph, Metrics, Size, FontKey, GlyphKey, Weight, Slant, Style};

/// Rasterizes glyphs for a single font face.
pub struct FreeTypeRasterizer {
    faces: HashMap<FontKey, Face<'static>>,
    library: Library,
    keys: HashMap<::std::path::PathBuf, FontKey>,
    dpi_x: u32,
    dpi_y: u32,
    dpr: f32,
}

#[inline]
fn to_freetype_26_6(f: f32) -> isize {
    ((1i32 << 6) as f32 * f) as isize
}

impl ::Rasterize for FreeTypeRasterizer {
    type Err = Error;

    fn new(dpi_x: f32, dpi_y: f32, device_pixel_ratio: f32, _: bool) -> Result<FreeTypeRasterizer, Error> {
        let library = Library::init()?;

        Ok(FreeTypeRasterizer {
            faces: HashMap::new(),
            keys: HashMap::new(),
            library: library,
            dpi_x: dpi_x as u32,
            dpi_y: dpi_y as u32,
            dpr: device_pixel_ratio,
        })
    }

    fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
        let face = self.faces
            .get(&key)
            .ok_or(Error::FontNotLoaded)?;

        let scale_size = self.dpr as f64 * size.as_f32_pts() as f64;

        let em_size = face.em_size() as f64;
        let w = face.max_advance_width() as f64;
        let h = (face.ascender() - face.descender() + face.height()) as f64;

        let w_scale = w * scale_size / em_size;
        let h_scale = h * scale_size / em_size;

        Ok(Metrics {
            average_advance: w_scale,
            line_height: h_scale,
        })
    }

    fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
        let face = self.get_face(desc)?;
        let key = FontKey::next();
        self.faces.insert(key, face);
        Ok(key)
    }

    fn get_glyph(&mut self, glyph_key: &GlyphKey) -> Result<RasterizedGlyph, Error> {
        self.get_rendered_glyph(glyph_key, false)
    }

}

pub trait IntoFontconfigType {
    type FcType;
    fn into_fontconfig_type(&self) -> Self::FcType;
}

impl IntoFontconfigType for Slant {
    type FcType = fc::Slant;
    fn into_fontconfig_type(&self) -> Self::FcType {
        match *self {
            Slant::Normal => fc::Slant::Roman,
            Slant::Italic => fc::Slant::Italic,
            Slant::Oblique => fc::Slant::Oblique,
        }
    }
}

impl IntoFontconfigType for Weight {
    type FcType = fc::Weight;

    fn into_fontconfig_type(&self) -> Self::FcType {
        match *self {
            Weight::Normal => fc::Weight::Regular,
            Weight::Bold => fc::Weight::Bold,
        }
    }
}

impl FreeTypeRasterizer {
    /// Load a font face accoring to `FontDesc`
    fn get_face(&mut self, desc: &FontDesc) -> Result<Face<'static>, Error> {
        match desc.style {
            Style::Description { slant, weight } => {
                // Match nearest font
                self.get_matching_face(&desc, slant, weight)
            }
            Style::Specific(ref style) => {
                // If a name was specified, try and load specifically that font.
                self.get_specific_face(&desc, &style)
            }
        }
    }

    fn get_matching_face(
        &mut self,
        desc: &FontDesc,
        slant: Slant,
        weight: Weight
    ) -> Result<Face<'static>, Error> {
        let mut pattern = fc::Pattern::new();
        pattern.add_family(&desc.name);
        pattern.set_weight(weight.into_fontconfig_type());
        pattern.set_slant(slant.into_fontconfig_type());

        let fonts = fc::font_sort(fc::Config::get_current(), &mut pattern)
            .ok_or_else(|| Error::MissingFont(desc.to_owned()))?;

        // Take first font that has a path
        for font in &fonts {
            if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
                return Ok(self.library.new_face(path, index)?);
            }
        }

        Err(Error::MissingFont(desc.to_owned()))
    }

    fn get_specific_face(
        &mut self,
        desc: &FontDesc,
        style: &str
    ) -> Result<Face<'static>, Error> {
        let mut pattern = fc::Pattern::new();
        pattern.add_family(&desc.name);
        pattern.add_style(style);

        let font = fc::font_match(fc::Config::get_current(), &mut pattern)
            .ok_or_else(|| Error::MissingFont(desc.to_owned()))?;
        if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
            println!("got font path={:?}", path);
            return Ok(self.library.new_face(path, index)?);
        } else {
            Err(Error::MissingFont(desc.to_owned()))
        }
    }

    fn get_rendered_glyph(&mut self, glyph_key: &GlyphKey, have_recursed: bool)
                          -> Result<RasterizedGlyph, Error> {
        let faces = self.faces.clone();
        let face = faces
            .get(&glyph_key.font_key)
            .ok_or(Error::FontNotLoaded)?;

        let size = glyph_key.size.as_f32_pts() * self.dpr;
        let c = glyph_key.c;

        face.set_char_size(to_freetype_26_6(size), 0, self.dpi_x, self.dpi_y)?;
        let index = face.get_char_index(c as usize);

        if index == 0 && have_recursed == false {
            let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key);
            let new_glyph_key = GlyphKey {
                c: glyph_key.c,
                font_key: key,
                size: glyph_key.size
            };

            return self.get_rendered_glyph(&new_glyph_key, true);
        }

        face.load_glyph(index as u32, freetype::face::TARGET_LIGHT)?;
        let glyph = face.glyph();
        glyph.render_glyph(freetype::render_mode::RenderMode::Lcd)?;

        unsafe {
            let ft_lib = self.library.raw();
            freetype::ffi::FT_Library_SetLcdFilter(
                ft_lib,
                freetype::ffi::FT_LCD_FILTER_DEFAULT
            );
        }

        let bitmap = glyph.bitmap();
        let buf = bitmap.buffer();
        let pitch = bitmap.pitch() as usize;

        let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize);
        for i in 0..bitmap.rows() {
            let start = (i as usize) * pitch;
            let stop = start + bitmap.width() as usize;
            packed.extend_from_slice(&buf[start..stop]);
        }

        Ok(RasterizedGlyph {
            c: c,
            top: glyph.bitmap_top(),
            left: glyph.bitmap_left(),
            width: glyph.bitmap().width() / 3,
            height: glyph.bitmap().rows(),
            buf: packed,
        })
    }

    fn load_face_with_glyph(&mut self, glyph: char) -> Result<FontKey, Error> {
        let mut charset = fc::CharSet::new();
        charset.add(glyph);
        let mut pattern = fc::Pattern::new();
        unsafe {
            pattern.add_charset(&charset);
        }

        let config = fc::Config::get_current();
        match fc::font_match(config, &mut pattern) {
            Some(font) => {
                if let (Some(path), Some(index)) = (font.file(0), font.index(0)) {
                    match self.keys.get(&path.to_path_buf()) {
                        // We've previously loaded this font, so don't
                        // load it again.
                        Some(&key) => {
                            debug!("Hit for font {:?}", path);
                            return Ok(key)
                        },

                        None => {
                            debug!("Miss for font {:?}", path);
                            let pathbuf = path.to_path_buf();
                            let face = self.library.new_face(path, index)?;
                            let key = FontKey::next();
                            self.keys.insert(pathbuf, key);
                            self.faces.insert(key, face);
                            return Ok(key)
                        }
                    }
                }

                Err(Error::MissingFont(
                    FontDesc::new("fallback-without-path", Style::Specific(glyph.to_string()))
                ))
            },
            None => {
                Err(Error::MissingFont(
                    FontDesc::new("no-fallback-for", Style::Specific(glyph.to_string()))
                ))
            }
        }
    }
}

/// Errors occurring when using the freetype rasterizer
#[derive(Debug)]
pub enum Error {
    /// Error occurred within the FreeType library
    FreeType(freetype::Error),

    /// Couldn't find font matching description
    MissingFont(FontDesc),

    /// Requested an operation with a FontKey that isn't known to the rasterizer
    FontNotLoaded,
}

impl ::std::error::Error for Error {
    fn cause(&self) -> Option<&::std::error::Error> {
        match *self {
            Error::FreeType(ref err) => Some(err),
            _ => None,
        }
    }

    fn description(&self) -> &str {
        match *self {
            Error::FreeType(ref err) => err.description(),
            Error::MissingFont(ref _desc) => "couldn't find the requested font",
            Error::FontNotLoaded => "tried to operate on font that hasn't been loaded",
        }
    }
}

impl ::std::fmt::Display for Error {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match *self {
            Error::FreeType(ref err) => {
                err.fmt(f)
            },
            Error::MissingFont(ref desc) => {
                write!(f, "Couldn't find a font with {}\
                       \n\tPlease check the font config in your alacritty.yml.", desc)
            },
            Error::FontNotLoaded => {
                f.write_str("Tried to use a font that hasn't been loaded")
            }
        }
    }
}

impl From<freetype::Error> for Error {
    fn from(val: freetype::Error) -> Error {
        Error::FreeType(val)
    }
}

unsafe impl Send for FreeTypeRasterizer {}
<|endoftext|>


<commit_before>use super::ease::Easing;

pub struct Cubic;

impl Easing for Cubic {
    fn ease_in(t: f32, b: f32, c: f32, d: f32) -> f32 {
        let inner_t = t / d;
        c * inner_t.powi(3) + b
    }

    fn ease_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        let inner_t = t / d - 1.0;
        c * (inner_t.powi(3) + 1.0) + b
    }

    fn ease_in_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        let mut inner_t = t / (d / 2.0);
        if inner_t < 1.0 {
            return c / 2.0 * inner_t.powi(3) + b;
        }
        inner_t -= 2.0;
        return c / 2.0 * (inner_t.powi(3) + 2.0) + b;
    }
}

mod test {
    use functions::ease::Easing;
    #[test]
    fn ease_in() {
        assert_relative_eq!(super::Cubic::ease_in(1.0, 2.0, 3.0, 4.0), 2.046875);
    }

    #[test]
    fn ease_out() {
        assert_relative_eq!(super::Cubic::ease_out(1.0, 2.0, 3.0, 4.0), 3.734375);
    }

    #[test]
    fn ease_in_out() {
        assert_relative_eq!(super::Cubic::ease_in_out(1.0, 2.0, 3.0, 4.0), 2.187500);
    }
}
<commit_msg>cubic function test added<commit_after>use super::ease::Easing;

pub struct Cubic;

impl Easing for Cubic {
    fn ease_in(t: f32, b: f32, c: f32, d: f32) -> f32 {
        let inner_t = t / d;
        c * inner_t.powi(3) + b
    }

    fn ease_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        let inner_t = t / d - 1.0;
        c * (inner_t.powi(3) + 1.0) + b
    }

    fn ease_in_out(t: f32, b: f32, c: f32, d: f32) -> f32 {
        let mut inner_t = t / (d / 2.0);
        if inner_t < 1.0 {
            return c / 2.0 * inner_t.powi(3) + b;
        }
        inner_t = inner_t - 2.0;
        return c / 2.0 * (inner_t.powi(3) + 2.0) + b;
    }
}

mod test {
    use functions::ease::Easing;
    #[test]
    fn ease_in() {
        assert_relative_eq!(super::Cubic::ease_in(1.0, 2.0, 3.0, 4.0), 2.046875);
    }

    #[test]
    fn ease_out() {
        assert_relative_eq!(super::Cubic::ease_out(1.0, 2.0, 3.0, 4.0), 3.734375);
    }

    #[test]
    fn ease_in_out() {
        assert_relative_eq!(super::Cubic::ease_in_out(1.0, 2.0, 3.0, 4.0), 2.187500);
        assert_relative_eq!(super::Cubic::ease_in_out(51.0, 1.0, 100.0, 100.0),
                            53.940397);
    }
}
<|endoftext|>


<commit_before>//! Test saving "default" and specific quality jpeg.
#![cfg(feature = "jpeg")]
extern crate image;

use image::{ImageOutputFormat, JPEG};

#[test]
fn jqeg_qualitys() {
    let img = image::open("tests/images/tiff/testsuite/lenna.tiff").unwrap();

    let mut default = vec![];
    img.save(&mut default, JPEG).unwrap();
    assert_eq!(&[255, 216], &default[..2]);

    let mut small = vec![];
    img.save(&mut small, ImageOutputFormat::JPEG(10)).unwrap();
    assert_eq!(&[255, 216], &small[..2]);

    assert!(small.len() < default.len());

    let mut large = vec![];
    img.save(&mut large, ImageOutputFormat::JPEG(99)).unwrap();
    assert_eq!(&[255, 216], &large[..2]);

    assert!(large.len() > default.len());
}
<commit_msg>Testing save_jpeg requires tiff support.<commit_after>//! Test saving "default" and specific quality jpeg.
#![cfg(all(feature = "jpeg", feature = "tiff"))]
extern crate image;

use image::{ImageOutputFormat, JPEG};

#[test]
fn jqeg_qualitys() {
    let img = image::open("tests/images/tiff/testsuite/lenna.tiff").unwrap();

    let mut default = vec![];
    img.save(&mut default, JPEG).unwrap();
    assert_eq!(&[255, 216], &default[..2]);

    let mut small = vec![];
    img.save(&mut small, ImageOutputFormat::JPEG(10)).unwrap();
    assert_eq!(&[255, 216], &small[..2]);

    assert!(small.len() < default.len());

    let mut large = vec![];
    img.save(&mut large, ImageOutputFormat::JPEG(99)).unwrap();
    assert_eq!(&[255, 216], &large[..2]);

    assert!(large.len() > default.len());
}
<|endoftext|>


<commit_before>extern crate alloc;

use crate::{
	block_current_task, get_priority, getpid, recmutex_destroy, recmutex_init, recmutex_lock,
	recmutex_unlock, wakeup_task, yield_now, Priority, Tid, NO_PRIORITIES,
};
use alloc::collections::vec_deque::VecDeque;
use core::cell::UnsafeCell;
use core::ffi::c_void;
use core::hint;
use core::ops::{Deref, DerefMut, Drop};
use core::ptr;
use core::sync::atomic::{AtomicUsize, Ordering};

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completely fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
	queue: AtomicUsize,
	dequeue: AtomicUsize,
	data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
	dequeue: &'a AtomicUsize,
	data: &'a mut T,
}

impl<T> Spinlock<T> {
	pub const fn new(user_data: T) -> Spinlock<T> {
		Spinlock {
			queue: AtomicUsize::new(0),
			dequeue: AtomicUsize::new(1),
			data: UnsafeCell::new(user_data),
		}
	}

	#[inline]
	fn obtain_lock(&self) {
		let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
		let mut counter: u16 = 0;
		while self.dequeue.load(Ordering::SeqCst) != ticket {
			counter += 1;
			if counter < 100 {
				hint::spin_loop();
			} else {
				counter = 0;
				unsafe {
					yield_now();
				}
			}
		}
	}

	#[inline]
	pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
		self.obtain_lock();
		SpinlockGuard {
			dequeue: &self.dequeue,
			data: &mut *self.data.get(),
		}
	}
}

impl<T: ?Sized + Default> Default for Spinlock<T> {
	fn default() -> Spinlock<T> {
		Spinlock::new(Default::default())
	}
}

impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
	type Target = T;
	fn deref(&self) -> &T {
		&*self.data
	}
}

impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
	fn deref_mut(&mut self) -> &mut T {
		&mut *self.data
	}
}

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
	/// The dropping of the SpinlockGuard will release the lock it was created from.
	fn drop(&mut self) {
		self.dequeue.fetch_add(1, Ordering::SeqCst);
	}
}

/// Realize a priority queue for tasks
pub struct PriorityQueue {
	queues: [Option<VecDeque<Tid>>; NO_PRIORITIES],
	prio_bitmap: u64,
}

impl PriorityQueue {
	pub const fn new() -> PriorityQueue {
		PriorityQueue {
			queues: [
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None,
			],
			prio_bitmap: 0,
		}
	}

	/// Add a task id by its priority to the queue
	pub fn push(&mut self, prio: Priority, id: Tid) {
		let i: usize = prio.into().into();
		self.prio_bitmap |= (1 << i) as u64;
		if let Some(queue) = &mut self.queues[i] {
			queue.push_back(id);
		} else {
			let mut queue = VecDeque::new();
			queue.push_back(id);
			self.queues[i] = Some(queue);
		}
	}

	fn pop_from_queue(&mut self, queue_index: usize) -> Option<Tid> {
		if let Some(queue) = &mut self.queues[queue_index] {
			let id = queue.pop_front();

			if queue.is_empty() {
				self.prio_bitmap &= !(1 << queue_index as u64);
			}

			id
		} else {
			None
		}
	}

	/// Pop the task handle with the highest priority from the queue
	pub fn pop(&mut self) -> Option<Tid> {
		for i in 0..NO_PRIORITIES {
			if self.prio_bitmap & (1 << i) != 0 {
				return self.pop_from_queue(i);
			}
		}

		None
	}
}

struct MutexInner {
	locked: bool,
	blocked_task: PriorityQueue,
}

impl MutexInner {
	pub const fn new() -> MutexInner {
		MutexInner {
			locked: false,
			blocked_task: PriorityQueue::new(),
		}
	}
}

pub struct Mutex {
	inner: Spinlock<MutexInner>,
}

pub type MovableMutex = Mutex;

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
	pub const fn new() -> Mutex {
		Mutex {
			inner: Spinlock::new(MutexInner::new()),
		}
	}

	#[inline]
	pub unsafe fn init(&mut self) {
		self.inner = Spinlock::new(MutexInner::new());
	}

	#[inline]
	pub unsafe fn lock(&self) {
		loop {
			let mut guard = self.inner.lock();
			if guard.locked == false {
				guard.locked = true;
				return;
			} else {
				let prio = get_priority();
				let id = getpid();

				guard.blocked_task.push(prio, id);
				block_current_task();
				drop(guard);
				yield_now();
			}
		}
	}

	#[inline]
	pub unsafe fn unlock(&self) {
		let mut guard = self.inner.lock();
		guard.locked = false;
		if let Some(tid) = guard.blocked_task.pop() {
			wakeup_task(tid);
		}
	}

	#[inline]
	pub unsafe fn try_lock(&self) -> bool {
		let mut guard = self.inner.lock();
		if guard.locked == false {
			guard.locked = true;
		}
		guard.locked
	}

	#[inline]
	pub unsafe fn destroy(&self) {}
}

pub struct ReentrantMutex {
	inner: *const c_void,
}

impl ReentrantMutex {
	pub const unsafe fn uninitialized() -> ReentrantMutex {
		ReentrantMutex { inner: ptr::null() }
	}

	#[inline]
	pub unsafe fn init(&self) {
		let _ = recmutex_init(&self.inner as *const *const c_void as *mut _);
	}

	#[inline]
	pub unsafe fn lock(&self) {
		let _ = recmutex_lock(self.inner);
	}

	#[inline]
	pub unsafe fn try_lock(&self) -> bool {
		true
	}

	#[inline]
	pub unsafe fn unlock(&self) {
		let _ = recmutex_unlock(self.inner);
	}

	#[inline]
	pub unsafe fn destroy(&self) {
		let _ = recmutex_destroy(self.inner);
	}
}
<commit_msg>remove clippy warnings and unneeded code<commit_after>extern crate alloc;

use crate::{
	block_current_task, get_priority, getpid, wakeup_task, yield_now, Priority, Tid, NO_PRIORITIES,
};
use alloc::collections::vec_deque::VecDeque;
use core::cell::UnsafeCell;
use core::hint;
use core::ops::{Deref, DerefMut, Drop};
use core::sync::atomic::{AtomicUsize, Ordering};

/// This type provides a lock based on busy waiting to realize mutual exclusion
///
/// # Description
///
/// This structure behaves a lot like a common mutex. There are some differences:
///
/// - By using busy waiting, it can be used outside the runtime.
/// - It is a so called ticket lock and is completely fair.
#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
struct Spinlock<T: ?Sized> {
	queue: AtomicUsize,
	dequeue: AtomicUsize,
	data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
struct SpinlockGuard<'a, T: ?Sized + 'a> {
	dequeue: &'a AtomicUsize,
	data: &'a mut T,
}

impl<T> Spinlock<T> {
	pub const fn new(user_data: T) -> Spinlock<T> {
		Spinlock {
			queue: AtomicUsize::new(0),
			dequeue: AtomicUsize::new(1),
			data: UnsafeCell::new(user_data),
		}
	}

	#[inline]
	fn obtain_lock(&self) {
		let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
		let mut counter: u16 = 0;
		while self.dequeue.load(Ordering::SeqCst) != ticket {
			counter += 1;
			if counter < 100 {
				hint::spin_loop();
			} else {
				counter = 0;
				unsafe {
					yield_now();
				}
			}
		}
	}

	#[inline]
	pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
		self.obtain_lock();
		SpinlockGuard {
			dequeue: &self.dequeue,
			data: &mut *self.data.get(),
		}
	}
}

impl<T: ?Sized + Default> Default for Spinlock<T> {
	fn default() -> Spinlock<T> {
		Spinlock::new(Default::default())
	}
}

impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
	type Target = T;
	fn deref(&self) -> &T {
		&*self.data
	}
}

impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
	fn deref_mut(&mut self) -> &mut T {
		&mut *self.data
	}
}

impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
	/// The dropping of the SpinlockGuard will release the lock it was created from.
	fn drop(&mut self) {
		self.dequeue.fetch_add(1, Ordering::SeqCst);
	}
}

/// Realize a priority queue for tasks
pub struct PriorityQueue {
	queues: [Option<VecDeque<Tid>>; NO_PRIORITIES],
	prio_bitmap: u64,
}

impl PriorityQueue {
	pub const fn new() -> PriorityQueue {
		PriorityQueue {
			queues: [
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None, None, None, None, None, None, None, None, None, None, None, None,
				None, None, None,
			],
			prio_bitmap: 0,
		}
	}

	/// Add a task id by its priority to the queue
	pub fn push(&mut self, prio: Priority, id: Tid) {
		let i: usize = prio.into().into();
		self.prio_bitmap |= (1 << i) as u64;
		if let Some(queue) = &mut self.queues[i] {
			queue.push_back(id);
		} else {
			let mut queue = VecDeque::new();
			queue.push_back(id);
			self.queues[i] = Some(queue);
		}
	}

	fn pop_from_queue(&mut self, queue_index: usize) -> Option<Tid> {
		if let Some(queue) = &mut self.queues[queue_index] {
			let id = queue.pop_front();

			if queue.is_empty() {
				self.prio_bitmap &= !(1 << queue_index as u64);
			}

			id
		} else {
			None
		}
	}

	/// Pop the task handle with the highest priority from the queue
	pub fn pop(&mut self) -> Option<Tid> {
		for i in 0..NO_PRIORITIES {
			if self.prio_bitmap & (1 << i) != 0 {
				return self.pop_from_queue(i);
			}
		}

		None
	}
}

struct MutexInner {
	locked: bool,
	blocked_task: PriorityQueue,
}

impl MutexInner {
	pub const fn new() -> MutexInner {
		MutexInner {
			locked: false,
			blocked_task: PriorityQueue::new(),
		}
	}
}

pub struct Mutex {
	inner: Spinlock<MutexInner>,
}

pub type MovableMutex = Mutex;

unsafe impl Send for Mutex {}
unsafe impl Sync for Mutex {}

impl Mutex {
	pub const fn new() -> Mutex {
		Mutex {
			inner: Spinlock::new(MutexInner::new()),
		}
	}

	#[inline]
	pub unsafe fn init(&mut self) {
		self.inner = Spinlock::new(MutexInner::new());
	}

	#[inline]
	pub unsafe fn lock(&self) {
		loop {
			let mut guard = self.inner.lock();
			if !guard.locked {
				guard.locked = true;
				return;
			} else {
				let prio = get_priority();
				let id = getpid();

				guard.blocked_task.push(prio, id);
				block_current_task();
				drop(guard);
				yield_now();
			}
		}
	}

	#[inline]
	pub unsafe fn unlock(&self) {
		let mut guard = self.inner.lock();
		guard.locked = false;
		if let Some(tid) = guard.blocked_task.pop() {
			wakeup_task(tid);
		}
	}

	#[inline]
	pub unsafe fn try_lock(&self) -> bool {
		let mut guard = self.inner.lock();
		if guard.locked == false {
			guard.locked = true;
		}
		guard.locked
	}

	#[inline]
	pub unsafe fn destroy(&self) {}
}
<|endoftext|>