<commit_before>#[macro_use]
use quote;
use syn;

#[derive(Default, PartialEq, Debug)]
struct GlobalAttrData {
    default_prefix: Option<String>,
    with_default: bool,
}

impl GlobalAttrData {
    fn from_attributes(attrs: &Vec<syn::Attribute>) -> GlobalAttrData {
        let builder_ident = syn::Ident::new("Builder".to_string());
        let derive_ident = syn::Ident::new("derive".to_string());
        let default_ident = syn::Ident::new("Default".to_string());
        let prefix_ident = syn::Ident::new("prefix".to_string());
        let mut data = GlobalAttrData {
            default_prefix: None,
            with_default: false,
        };

        use syn::MetaItem::*;
        use syn::Lit::*;

        // OMFG: So much nesting
        for attr in attrs.iter() {
            if let List(ref ident, ref items) = attr.value {
                if ident == &derive_ident {
                    for attr_item in items.iter() {
                        if let &List(ref ident, _) = attr_item {
                            if *ident == default_ident {
                                data.with_default = true;
                            }
                        }
                    }
                }
            }
            if let NameValue(ref key, Str(ref prefix, _)) = attr.value {
                if *key == prefix_ident {
                    data.default_prefix = Some(prefix.clone());
                }
            }
        }
        data
    }
}

#[derive(Debug, PartialEq, Default)]
struct FieldAttrData {
    field_name: Option<String>,
    create_field: bool,
}

impl FieldAttrData {
    fn from_attributes(attrs: &Vec<syn::Attribute>) -> FieldAttrData {
        use syn::MetaItem::*;
        use syn::Lit::*;
        let mut data: FieldAttrData = FieldAttrData {
            create_field: true,
            .. Default::default()
        };
        let field_name_ident = syn::Ident::new("field_name".to_string());
        let create_field_ident = syn::Ident::new("should_create".to_string());
        for attr in attrs.iter() {
            match &attr.value {
                &NameValue(ref key, Str(ref value, _)) => {
                    if field_name_ident == key {
                        data.field_name = Some(value.clone());
                    }
                },
                &NameValue(ref key, Bool(ref value)) => {
                    if create_field_ident == key {
                        data.create_field = value.clone();
                    }
                },
                _ => ()
            }
        }

        data
    }
}

#[derive(PartialEq, Debug)]
struct CodeGenData<'a> {
    attr_data: &'a GlobalAttrData,
    fields: &'a Vec<syn::Field>,
    struct_name: &'a syn::Ident,
    ty_generics: &'a syn::Generics,
}

fn expand_fields<F>(fields: &Vec<syn::Field>, f: F) -> quote::Tokens
    where F: Fn(&syn::Field) -> quote::Tokens {
    let mut tokens = quote::Tokens::new();
    tokens.append_all(fields.iter().map(f));
    tokens
}

fn get_method_name(field_attrs: &FieldAttrData, field: &syn::Field, mutable: bool, prefix: &Option<String>) -> syn::Ident {
    let _prefix = match prefix {
        &Some(ref string) => string.clone(),
        &None => "".to_string(),
    };
    let base_name = match field_attrs.field_name {
        Some(ref field_name) => {
            format!("{}{}", _prefix, field_name)
        },
        None => {
            format!("{}{}", _prefix, &field.ident.as_ref().unwrap())
        }
    };

    if mutable {
        syn::Ident::new(format!("{}_mut", base_name))
    } else {
        syn::Ident::new(base_name)
    }
}

fn expand_mutable(data: &CodeGenData, fields: &Vec<syn::Field>) -> quote::Tokens {
    let prefix = &data.attr_data.default_prefix;
    expand_fields(fields, |field| {
        let field_attrs = FieldAttrData::from_attributes(&field.attrs);
        let ident = field.ident.as_ref().unwrap();
        if field_attrs.create_field {
            let method_name = get_method_name(&field_attrs, field, true, &prefix);
            let ty = &field.ty;
            let struct_name = data.struct_name;
            let ty_generics = data.ty_generics;
            quote! {
                fn #method_name(&mut self, __value: #ty) -> &mut #struct_name #ty_generics {
                    self. #ident = __value;
                    self
                }
            }
        } else {
            quote! {
                // Omitted #ident
            }
        }
    })
}

fn expand_immutable(data: &CodeGenData, fields: &Vec<syn::Field>) -> quote::Tokens {
    let prefix = &data.attr_data.default_prefix;
    expand_fields(fields, |field| {
        let attr_data = FieldAttrData::from_attributes(&field.attrs);
        let ident = field.ident.as_ref().unwrap();
        if attr_data.create_field {
            let ty = &field.ty;
            let struct_name = data.struct_name;
            let ty_generics = data.ty_generics;
            let method_name = get_method_name(&attr_data, &field, false, &prefix);
            quote! {
                fn #method_name(self, __value: #ty) -> #struct_name #ty_generics {
                    #struct_name { #ident : __value, .. self }
                }
            }
        } else {
            quote! {
                // something is silly here
            }
        }
    })
}

fn expand_methods(ast: &syn::MacroInput) -> quote::Tokens {
    let body = &ast.body;
    match body {
        &syn::Body::Struct(ref variant) => {
            match variant {
                &syn::VariantData::Struct(ref fields) => {
                    let struct_name = &ast.ident;
                    let (_, ty_generics, _) = ast.generics.split_for_impl();
                    let attr_data = GlobalAttrData::from_attributes(&ast.attrs);
                    let gen_data = CodeGenData {
                        ty_generics: &ty_generics,
                        struct_name: &struct_name,
                        attr_data: &attr_data,
                        fields: fields,
                    };
                    let immutable_build_methods = expand_immutable(&gen_data, fields);
                    let mutable_build_methods = expand_mutable(&gen_data, fields);
                    quote! {
                        #immutable_build_methods

                        #mutable_build_methods
                    }
                },
                _ => panic!("Derive builder is not supported for tuple structs and the unit struct")
            }
        },
        &syn::Body::Enum(_) => panic!("Enums are not supported"),
    }
}

pub fn expand_builder(ast: syn::MacroInput) -> quote::Tokens {
    let methods = expand_methods(&ast);
    let struct_name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    quote! {
        #[allow(unused_attributes)]
        #ast

        #[allow(dead_code)]
        impl #impl_generics #struct_name #ty_generics #where_clause {
            #methods
        }
    }
}
<commit_msg>Removed check that struct has "Default" derived.<commit_after>#[macro_use]
use quote;
use syn;

#[derive(Default, PartialEq, Debug)]
struct GlobalAttrData {
    default_prefix: Option<String>,
}

impl GlobalAttrData {
    fn from_attributes(attrs: &Vec<syn::Attribute>) -> GlobalAttrData {
        let prefix_ident = syn::Ident::new("prefix".to_string());
        let mut data = GlobalAttrData {
            default_prefix: None,
        };

        use syn::MetaItem::*;
        use syn::Lit::*;

        for attr in attrs.iter() {
            if let NameValue(ref key, Str(ref prefix, _)) = attr.value {
                if *key == prefix_ident {
                    data.default_prefix = Some(prefix.clone());
                }
            }
        }
        data
    }
}

#[derive(Debug, PartialEq, Default)]
struct FieldAttrData {
    field_name: Option<String>,
    create_field: bool,
}

impl FieldAttrData {
    fn from_attributes(attrs: &Vec<syn::Attribute>) -> FieldAttrData {
        use syn::MetaItem::*;
        use syn::Lit::*;
        let mut data: FieldAttrData = FieldAttrData {
            create_field: true,
            .. Default::default()
        };
        let field_name_ident = syn::Ident::new("field_name".to_string());
        let create_field_ident = syn::Ident::new("should_create".to_string());
        for attr in attrs.iter() {
            match &attr.value {
                &NameValue(ref key, Str(ref value, _)) => {
                    if field_name_ident == key {
                        data.field_name = Some(value.clone());
                    }
                },
                &NameValue(ref key, Bool(ref value)) => {
                    if create_field_ident == key {
                        data.create_field = value.clone();
                    }
                },
                _ => ()
            }
        }

        data
    }
}

#[derive(PartialEq, Debug)]
struct CodeGenData<'a> {
    attr_data: &'a GlobalAttrData,
    fields: &'a Vec<syn::Field>,
    struct_name: &'a syn::Ident,
    ty_generics: &'a syn::Generics,
}

fn expand_fields<F>(fields: &Vec<syn::Field>, f: F) -> quote::Tokens
    where F: Fn(&syn::Field) -> quote::Tokens {
    let mut tokens = quote::Tokens::new();
    tokens.append_all(fields.iter().map(f));
    tokens
}

fn get_method_name(field_attrs: &FieldAttrData, field: &syn::Field, mutable: bool, prefix: &Option<String>) -> syn::Ident {
    let _prefix = match prefix {
        &Some(ref string) => string.clone(),
        &None => "".to_string(),
    };
    let base_name = match field_attrs.field_name {
        Some(ref field_name) => {
            format!("{}{}", _prefix, field_name)
        },
        None => {
            format!("{}{}", _prefix, &field.ident.as_ref().unwrap())
        }
    };

    if mutable {
        syn::Ident::new(format!("{}_mut", base_name))
    } else {
        syn::Ident::new(base_name)
    }
}

fn expand_mutable(data: &CodeGenData, fields: &Vec<syn::Field>) -> quote::Tokens {
    let prefix = &data.attr_data.default_prefix;
    expand_fields(fields, |field| {
        let field_attrs = FieldAttrData::from_attributes(&field.attrs);
        let ident = field.ident.as_ref().unwrap();
        if field_attrs.create_field {
            let method_name = get_method_name(&field_attrs, field, true, &prefix);
            let ty = &field.ty;
            let struct_name = data.struct_name;
            let ty_generics = data.ty_generics;
            quote! {
                fn #method_name(&mut self, __value: #ty) -> &mut #struct_name #ty_generics {
                    self. #ident = __value;
                    self
                }
            }
        } else {
            quote! {
                // Omitted #ident
            }
        }
    })
}

fn expand_immutable(data: &CodeGenData, fields: &Vec<syn::Field>) -> quote::Tokens {
    let prefix = &data.attr_data.default_prefix;
    expand_fields(fields, |field| {
        let attr_data = FieldAttrData::from_attributes(&field.attrs);
        let ident = field.ident.as_ref().unwrap();
        if attr_data.create_field {
            let ty = &field.ty;
            let struct_name = data.struct_name;
            let ty_generics = data.ty_generics;
            let method_name = get_method_name(&attr_data, &field, false, &prefix);
            quote! {
                fn #method_name(self, __value: #ty) -> #struct_name #ty_generics {
                    #struct_name { #ident : __value, .. self }
                }
            }
        } else {
            quote! {
                // something is silly here
            }
        }
    })
}

fn expand_methods(ast: &syn::MacroInput) -> quote::Tokens {
    let body = &ast.body;
    match body {
        &syn::Body::Struct(ref variant) => {
            match variant {
                &syn::VariantData::Struct(ref fields) => {
                    let struct_name = &ast.ident;
                    let (_, ty_generics, _) = ast.generics.split_for_impl();
                    let attr_data = GlobalAttrData::from_attributes(&ast.attrs);
                    let gen_data = CodeGenData {
                        ty_generics: &ty_generics,
                        struct_name: &struct_name,
                        attr_data: &attr_data,
                        fields: fields,
                    };
                    let immutable_build_methods = expand_immutable(&gen_data, fields);
                    let mutable_build_methods = expand_mutable(&gen_data, fields);
                    quote! {
                        #immutable_build_methods

                        #mutable_build_methods
                    }
                },
                _ => panic!("Derive builder is not supported for tuple structs and the unit struct")
            }
        },
        &syn::Body::Enum(_) => panic!("Enums are not supported"),
    }
}

pub fn expand_builder(ast: syn::MacroInput) -> quote::Tokens {
    let methods = expand_methods(&ast);
    let struct_name = &ast.ident;
    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
    quote! {
        #[allow(unused_attributes)]
        #ast

        #[allow(dead_code)]
        impl #impl_generics #struct_name #ty_generics #where_clause {
            #methods
        }
    }
}
<|endoftext|>


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

use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::ty::subst::Substs;
use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};

use super::ToRegionVid;
use super::free_regions::FreeRegions;

/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(
    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
    free_regions: &FreeRegions<'tcx>,
    mir: &mut Mir<'tcx>,
) {
    // Create inference variables for each of the free regions
    // declared on the function signature.
    let free_region_inference_vars = (0..free_regions.indices.len())
        .map(RegionVid::new)
        .map(|vid_expected| {
            let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
            assert_eq!(vid_expected, r.to_region_vid());
            r
        })
        .collect();

    let mut visitor = NLLVisitor {
        infcx,
        free_regions,
        free_region_inference_vars,
        arg_count: mir.arg_count,
    };
    visitor.visit_mir(mir);
}

struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
    free_regions: &'a FreeRegions<'tcx>,
    free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
    arg_count: usize,
}

impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
    /// Replaces all regions appearing in `value` with fresh inference
    /// variables. This is what we do for almost the entire MIR, with
    /// the exception of the declared types of our arguments.
    fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
    where
        T: TypeFoldable<'tcx>,
    {
        self.infcx
            .tcx
            .fold_regions(value, &mut false, |_region, _depth| {
                let origin = NLLRegionVariableOrigin::Inferred(ty_context);
                self.infcx.next_nll_region_var(origin)
            })
    }

    /// Renumbers the regions appearing in `value`, but those regions
    /// are expected to be free regions from the function signature.
    fn renumber_free_regions<T>(&mut self, value: &T) -> T
    where
        T: TypeFoldable<'tcx>,
    {
        self.infcx
            .tcx
            .fold_regions(value, &mut false, |region, _depth| {
                let index = self.free_regions.indices[&region];
                self.free_region_inference_vars[index]
            })
    }

    fn is_argument_or_return_slot(&self, local: Local) -> bool {
        // The first argument is return slot, next N are arguments.
        local.index() <= self.arg_count
    }
}

impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
        let is_arg = match ty_context {
            TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
            TyContext::ReturnTy(..) => true,
            TyContext::Location(..) => false,
        };
        debug!(
            "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})",
            ty,
            is_arg,
            ty_context
        );

        let old_ty = *ty;
        *ty = if is_arg {
            self.renumber_free_regions(&old_ty)
        } else {
            self.renumber_regions(ty_context, &old_ty)
        };
        debug!("visit_ty: ty={:?}", ty);
    }

    fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
        let ty_context = TyContext::Location(location);
        *substs = self.renumber_regions(ty_context, &{ *substs });
    }

    fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
        match *rvalue {
            Rvalue::Ref(ref mut r, _, _) => {
                let old_r = *r;
                let ty_context = TyContext::Location(location);
                *r = self.renumber_regions(ty_context, &old_r);
            }
            Rvalue::Use(..) |
            Rvalue::Repeat(..) |
            Rvalue::Len(..) |
            Rvalue::Cast(..) |
            Rvalue::BinaryOp(..) |
            Rvalue::CheckedBinaryOp(..) |
            Rvalue::UnaryOp(..) |
            Rvalue::Discriminant(..) |
            Rvalue::NullaryOp(..) |
            Rvalue::Aggregate(..) => {
                // These variants don't contain regions.
            }
        }
        self.super_rvalue(rvalue, location);
    }

    fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
        let ty_context = TyContext::Location(location);
        *substs = self.renumber_regions(ty_context, substs);
    }

    fn visit_statement(
        &mut self,
        block: BasicBlock,
        statement: &mut Statement<'tcx>,
        location: Location,
    ) {
        if let StatementKind::EndRegion(_) = statement.kind {
            statement.kind = StatementKind::Nop;
        }
        self.super_statement(block, statement, location);
    }
}
<commit_msg>renumber: debug logs, use `visit_region` rather than `visit_rvalue`<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc::ty::subst::Substs;
use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};

use super::ToRegionVid;
use super::free_regions::FreeRegions;

/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(
    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
    free_regions: &FreeRegions<'tcx>,
    mir: &mut Mir<'tcx>,
) {
    // Create inference variables for each of the free regions
    // declared on the function signature.
    let free_region_inference_vars = (0..free_regions.indices.len())
        .map(RegionVid::new)
        .map(|vid_expected| {
            let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
            assert_eq!(vid_expected, r.to_region_vid());
            r
        })
        .collect();

    debug!("renumber_mir()");
    debug!("renumber_mir: free_regions={:#?}", free_regions);
    debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);

    let mut visitor = NLLVisitor {
        infcx,
        free_regions,
        free_region_inference_vars,
        arg_count: mir.arg_count,
    };
    visitor.visit_mir(mir);
}

struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
    free_regions: &'a FreeRegions<'tcx>,
    free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
    arg_count: usize,
}

impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
    /// Replaces all regions appearing in `value` with fresh inference
    /// variables. This is what we do for almost the entire MIR, with
    /// the exception of the declared types of our arguments.
    fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
    where
        T: TypeFoldable<'tcx>,
    {
        debug!("renumber_regions(value={:?})", value);

        self.infcx
            .tcx
            .fold_regions(value, &mut false, |_region, _depth| {
                let origin = NLLRegionVariableOrigin::Inferred(ty_context);
                self.infcx.next_nll_region_var(origin)
            })
    }

    /// Renumbers the regions appearing in `value`, but those regions
    /// are expected to be free regions from the function signature.
    fn renumber_free_regions<T>(&mut self, value: &T) -> T
    where
        T: TypeFoldable<'tcx>,
    {
        debug!("renumber_free_regions(value={:?})", value);

        self.infcx
            .tcx
            .fold_regions(value, &mut false, |region, _depth| {
                let index = self.free_regions.indices[&region];
                self.free_region_inference_vars[index]
            })
    }

    fn is_argument_or_return_slot(&self, local: Local) -> bool {
        // The first argument is return slot, next N are arguments.
        local.index() <= self.arg_count
    }
}

impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
        let is_arg = match ty_context {
            TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
            TyContext::ReturnTy(..) => true,
            TyContext::Location(..) => false,
        };
        debug!(
            "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})",
            ty,
            is_arg,
            ty_context
        );

        let old_ty = *ty;
        *ty = if is_arg {
            self.renumber_free_regions(&old_ty)
        } else {
            self.renumber_regions(ty_context, &old_ty)
        };
        debug!("visit_ty: ty={:?}", ty);
    }

    fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
        debug!("visit_substs(substs={:?}, location={:?})", substs, location);

        let ty_context = TyContext::Location(location);
        *substs = self.renumber_regions(ty_context, &{ *substs });

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

    fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
        debug!("visit_region(region={:?}, location={:?})", region, location);

        let old_region = *region;
        let ty_context = TyContext::Location(location);
        *region = self.renumber_regions(ty_context, &old_region);

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

    fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
        debug!(
            "visit_closure_substs(substs={:?}, location={:?})",
            substs,
            location
        );

        let ty_context = TyContext::Location(location);
        *substs = self.renumber_regions(ty_context, substs);

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

    fn visit_statement(
        &mut self,
        block: BasicBlock,
        statement: &mut Statement<'tcx>,
        location: Location,
    ) {
        if let StatementKind::EndRegion(_) = statement.kind {
            statement.kind = StatementKind::Nop;
        }
        self.super_statement(block, statement, location);
    }
}
<|endoftext|>


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

// Characters and their corresponding confusables were collected from
// http://www.unicode.org/Public/security/revision-06/confusables.txt

use codemap::mk_sp as make_span;
use super::StringReader;

const UNICODE_ARRAY: &'static [(char, &'static str, char)] = &[
    ('ߺ', "Nko Lajanyalan", '_'),
    ('﹍', "Dashed Low Line", '_'),
    ('﹎', "Centreline Low Line", '_'),
    ('﹏', "Wavy Low Line", '_'),
    ('‐', "Hyphen", '-'),
    ('‑', "Non-Breaking Hyphen", '-'),
    ('‒', "Figure Dash", '-'),
    ('–', "En Dash", '-'),
    ('﹘', "Small Em Dash", '-'),
    ('⁃', "Hyphen Bullet", '-'),
    ('˗', "Modifier Letter Minus Sign", '-'),
    ('−', "Minus Sign", '-'),
    ('٫', "Arabic Decimal Separator", ','),
    ('‚', "Single Low-9 Quotation Mark", ','),
    ('ꓹ', "Lisu Letter Tone Na Po", ','),
    (';', "Greek Question Mark", ';'),
    ('ः', "Devanagari Sign Visarga", ':'),
    ('ઃ', "Gujarati Sign Visarga", ':'),
    ('：', "Fullwidth Colon", ':'),
    ('։', "Armenian Full Stop", ':'),
    ('܃', "Syriac Supralinear Colon", ':'),
    ('܄', "Syriac Sublinear Colon", ':'),
    ('︰', "Presentation Form For Vertical Two Dot Leader", ':'),
    ('᠃', "Mongolian Full Stop", ':'),
    ('᠉', "Mongolian Manchu Full Stop", ':'),
    ('⁚', "Two Dot Punctuation", ':'),
    ('׃', "Hebrew Punctuation Sof Pasuq", ':'),
    ('˸', "Modifier Letter Raised Colon", ':'),
    ('꞉', "Modifier Letter Colon", ':'),
    ('∶', "Ratio", ':'),
    ('ː', "Modifier Letter Triangular Colon", ':'),
    ('ꓽ', "Lisu Letter Tone Mya Jeu", ':'),
    ('！', "Fullwidth Exclamation Mark", '!'),
    ('ǃ', "Latin Letter Retroflex Click", '!'),
    ('ʔ', "Latin Letter Glottal Stop", '?'),
    ('ॽ', "Devanagari Letter Glottal Stop", '?'),
    ('Ꭾ', "Cherokee Letter He", '?'),
    ('𝅭', "Musical Symbol Combining Augmentation Dot", '.'),
    ('․', "One Dot Leader", '.'),
    ('۔', "Arabic Full Stop", '.'),
    ('܁', "Syriac Supralinear Full Stop", '.'),
    ('܂', "Syriac Sublinear Full Stop", '.'),
    ('꘎', "Vai Full Stop", '.'),
    ('𐩐', "Kharoshthi Punctuation Dot", '.'),
    ('٠', "Arabic-Indic Digit Zero", '.'),
    ('۰', "Extended Arabic-Indic Digit Zero", '.'),
    ('ꓸ', "Lisu Letter Tone Mya Ti", '.'),
    ('՝', "Armenian Comma", '\''),
    ('＇', "Fullwidth Apostrophe", '\''),
    ('‘', "Left Single Quotation Mark", '\''),
    ('’', "Right Single Quotation Mark", '\''),
    ('‛', "Single High-Reversed-9 Quotation Mark", '\''),
    ('′', "Prime", '\''),
    ('‵', "Reversed Prime", '\''),
    ('՚', "Armenian Apostrophe", '\''),
    ('׳', "Hebrew Punctuation Geresh", '\''),
    ('`', "Greek Varia", '\''),
    ('｀', "Fullwidth Grave Accent", '\''),
    ('΄', "Greek Tonos", '\''),
    ('´', "Greek Oxia", '\''),
    ('᾽', "Greek Koronis", '\''),
    ('᾿', "Greek Psili", '\''),
    ('῾', "Greek Dasia", '\''),
    ('ʹ', "Modifier Letter Prime", '\''),
    ('ʹ', "Greek Numeral Sign", '\''),
    ('ˊ', "Modifier Letter Acute Accent", '\''),
    ('ˋ', "Modifier Letter Grave Accent", '\''),
    ('˴', "Modifier Letter Middle Grave Accent", '\''),
    ('ʻ', "Modifier Letter Turned Comma", '\''),
    ('ʽ', "Modifier Letter Reversed Comma", '\''),
    ('ʼ', "Modifier Letter Apostrophe", '\''),
    ('ʾ', "Modifier Letter Right Half Ring", '\''),
    ('ꞌ', "Latin Small Letter Saltillo", '\''),
    ('י', "Hebrew Letter Yod", '\''),
    ('ߴ', "Nko High Tone Apostrophe", '\''),
    ('ߵ', "Nko Low Tone Apostrophe", '\''),
    ('［', "Fullwidth Left Square Bracket", '('),
    ('❨', "Medium Left Parenthesis Ornament", '('),
    ('❲', "Light Left Tortoise Shell Bracket Ornament", '('),
    ('〔', "Left Tortoise Shell Bracket", '('),
    ('﴾', "Ornate Left Parenthesis", '('),
    ('］', "Fullwidth Right Square Bracket", ')'),
    ('❩', "Medium Right Parenthesis Ornament", ')'),
    ('❳', "Light Right Tortoise Shell Bracket Ornament", ')'),
    ('〕', "Right Tortoise Shell Bracket", ')'),
    ('﴿', "Ornate Right Parenthesis", ')'),
    ('❴', "Medium Left Curly Bracket Ornament", '{'),
    ('❵', "Medium Right Curly Bracket Ornament", '}'),
    ('⁎', "Low Asterisk", '*'),
    ('٭', "Arabic Five Pointed Star", '*'),
    ('∗', "Asterisk Operator", '*'),
    ('᜵', "Philippine Single Punctuation", '/'),
    ('⁁', "Caret Insertion Point", '/'),
    ('∕', "Division Slash", '/'),
    ('⁄', "Fraction Slash", '/'),
    ('╱', "Box Drawings Light Diagonal Upper Right To Lower Left", '/'),
    ('⟋', "Mathematical Rising Diagonal", '/'),
    ('⧸', "Big Solidus", '/'),
    ('㇓', "Cjk Stroke Sp", '/'),
    ('〳', "Vertical Kana Repeat Mark Upper Half", '/'),
    ('丿', "Cjk Unified Ideograph-4E3F", '/'),
    ('⼃', "Kangxi Radical Slash", '/'),
    ('＼', "Fullwidth Reverse Solidus", '\\'),
    ('﹨', "Small Reverse Solidus", '\\'),
    ('∖', "Set Minus", '\\'),
    ('⟍', "Mathematical Falling Diagonal", '\\'),
    ('⧵', "Reverse Solidus Operator", '\\'),
    ('⧹', "Big Reverse Solidus", '\\'),
    ('㇔', "Cjk Stroke D", '\\'),
    ('丶', "Cjk Unified Ideograph-4E36", '\\'),
    ('⼂', "Kangxi Radical Dot", '\\'),
    ('ꝸ', "Latin Small Letter Um", '&'),
    ('﬩', "Hebrew Letter Alternative Plus Sign", '+'),
    ('‹', "Single Left-Pointing Angle Quotation Mark", '<'),
    ('❮', "Heavy Left-Pointing Angle Quotation Mark Ornament", '<'),
    ('˂', "Modifier Letter Left Arrowhead", '<'),
    ('꓿', "Lisu Punctuation Full Stop", '='),
    ('›', "Single Right-Pointing Angle Quotation Mark", '>'),
    ('❯', "Heavy Right-Pointing Angle Quotation Mark Ornament", '>'),
    ('˃', "Modifier Letter Right Arrowhead", '>'),
    ('Ⲻ', "Coptic Capital Letter Dialect-P Ni", '-'),
    ('Ɂ', "Latin Capital Letter Glottal Stop", '?'),
    ('Ⳇ', "Coptic Capital Letter Old Coptic Esh", '/'), ];

const ASCII_ARRAY: &'static [(char, &'static str)] = &[
    ('_', "Underscore"),
    ('-', "Minus/Hyphen"),
    (',', "Comma"),
    (';', "Semicolon"),
    (':', "Colon"),
    ('!', "Exclamation Mark"),
    ('?', "Question Mark"),
    ('.', "Period"),
    ('\'', "Single Quote"),
    ('(', "Left Parenthesis"),
    (')', "Right Parenthesis"),
    ('{', "Left Curly Brace"),
    ('}', "Right Curly Brace"),
    ('*', "Asterisk"),
    ('/', "Slash"),
    ('\\', "Backslash"),
    ('&', "Ampersand"),
    ('+', "Plus Sign"),
    ('<', "Less-Than Sign"),
    ('=', "Equals Sign"),
    ('>', "Greater-Than Sign"), ];

pub fn check_for_substitution(reader: &StringReader, ch: char) {
    UNICODE_ARRAY
    .iter()
    .find(|&&(c, _, _)| c == ch)
    .map(|&(_, u_name, ascii_char)| {
        let span = make_span(reader.last_pos, reader.pos);
        match ASCII_ARRAY.iter().find(|&&(c, _)| c == ascii_char) {
            Some(&(ascii_char, ascii_name)) => {
                let msg =
                    format!("unicode character '{}' ({}) looks much like '{}' ({}), but it's not",
                            ch, u_name, ascii_char, ascii_name);
                reader.help_span(span, &msg);
            },
            None => {
                reader
                .span_diagnostic
                .span_bug_no_panic(span,
                                   &format!("substitution character not found for '{}'", ch));
            }
        }
    });
}
<commit_msg>Add some unicode aliases for ".<commit_after>// Copyright 2012-2013 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.

// Characters and their corresponding confusables were collected from
// http://www.unicode.org/Public/security/revision-06/confusables.txt

use codemap::mk_sp as make_span;
use super::StringReader;

const UNICODE_ARRAY: &'static [(char, &'static str, char)] = &[
    ('ߺ', "Nko Lajanyalan", '_'),
    ('﹍', "Dashed Low Line", '_'),
    ('﹎', "Centreline Low Line", '_'),
    ('﹏', "Wavy Low Line", '_'),
    ('‐', "Hyphen", '-'),
    ('‑', "Non-Breaking Hyphen", '-'),
    ('‒', "Figure Dash", '-'),
    ('–', "En Dash", '-'),
    ('﹘', "Small Em Dash", '-'),
    ('⁃', "Hyphen Bullet", '-'),
    ('˗', "Modifier Letter Minus Sign", '-'),
    ('−', "Minus Sign", '-'),
    ('٫', "Arabic Decimal Separator", ','),
    ('‚', "Single Low-9 Quotation Mark", ','),
    ('ꓹ', "Lisu Letter Tone Na Po", ','),
    (';', "Greek Question Mark", ';'),
    ('ः', "Devanagari Sign Visarga", ':'),
    ('ઃ', "Gujarati Sign Visarga", ':'),
    ('：', "Fullwidth Colon", ':'),
    ('։', "Armenian Full Stop", ':'),
    ('܃', "Syriac Supralinear Colon", ':'),
    ('܄', "Syriac Sublinear Colon", ':'),
    ('︰', "Presentation Form For Vertical Two Dot Leader", ':'),
    ('᠃', "Mongolian Full Stop", ':'),
    ('᠉', "Mongolian Manchu Full Stop", ':'),
    ('⁚', "Two Dot Punctuation", ':'),
    ('׃', "Hebrew Punctuation Sof Pasuq", ':'),
    ('˸', "Modifier Letter Raised Colon", ':'),
    ('꞉', "Modifier Letter Colon", ':'),
    ('∶', "Ratio", ':'),
    ('ː', "Modifier Letter Triangular Colon", ':'),
    ('ꓽ', "Lisu Letter Tone Mya Jeu", ':'),
    ('！', "Fullwidth Exclamation Mark", '!'),
    ('ǃ', "Latin Letter Retroflex Click", '!'),
    ('ʔ', "Latin Letter Glottal Stop", '?'),
    ('ॽ', "Devanagari Letter Glottal Stop", '?'),
    ('Ꭾ', "Cherokee Letter He", '?'),
    ('𝅭', "Musical Symbol Combining Augmentation Dot", '.'),
    ('․', "One Dot Leader", '.'),
    ('۔', "Arabic Full Stop", '.'),
    ('܁', "Syriac Supralinear Full Stop", '.'),
    ('܂', "Syriac Sublinear Full Stop", '.'),
    ('꘎', "Vai Full Stop", '.'),
    ('𐩐', "Kharoshthi Punctuation Dot", '.'),
    ('٠', "Arabic-Indic Digit Zero", '.'),
    ('۰', "Extended Arabic-Indic Digit Zero", '.'),
    ('ꓸ', "Lisu Letter Tone Mya Ti", '.'),
    ('՝', "Armenian Comma", '\''),
    ('＇', "Fullwidth Apostrophe", '\''),
    ('‘', "Left Single Quotation Mark", '\''),
    ('’', "Right Single Quotation Mark", '\''),
    ('‛', "Single High-Reversed-9 Quotation Mark", '\''),
    ('′', "Prime", '\''),
    ('‵', "Reversed Prime", '\''),
    ('՚', "Armenian Apostrophe", '\''),
    ('׳', "Hebrew Punctuation Geresh", '\''),
    ('`', "Greek Varia", '\''),
    ('｀', "Fullwidth Grave Accent", '\''),
    ('΄', "Greek Tonos", '\''),
    ('´', "Greek Oxia", '\''),
    ('᾽', "Greek Koronis", '\''),
    ('᾿', "Greek Psili", '\''),
    ('῾', "Greek Dasia", '\''),
    ('ʹ', "Modifier Letter Prime", '\''),
    ('ʹ', "Greek Numeral Sign", '\''),
    ('ˊ', "Modifier Letter Acute Accent", '\''),
    ('ˋ', "Modifier Letter Grave Accent", '\''),
    ('˴', "Modifier Letter Middle Grave Accent", '\''),
    ('ʻ', "Modifier Letter Turned Comma", '\''),
    ('ʽ', "Modifier Letter Reversed Comma", '\''),
    ('ʼ', "Modifier Letter Apostrophe", '\''),
    ('ʾ', "Modifier Letter Right Half Ring", '\''),
    ('ꞌ', "Latin Small Letter Saltillo", '\''),
    ('י', "Hebrew Letter Yod", '\''),
    ('ߴ', "Nko High Tone Apostrophe", '\''),
    ('ߵ', "Nko Low Tone Apostrophe", '\''),
    ('＂', "Fullwidth Quotation Mark", '"'),
    ('“', "Left Double Quotation Mark", '"'),
    ('”', "Right Double Quotation Mark", '"'),
    ('‟', "Double High-Reversed-9 Quotation Mark", '"'),
    ('″', "Double Prime", '"'),
    ('‶', "Reversed Double Prime", '"'),
    ('〃', "Ditto Mark", '"'),
    ('״', "Hebrew Punctuation Gershayim", '"'),
    ('˝', "Double Acute Accent", '"'),
    ('ʺ', "Modifier Letter Double Prime", '"'),
    ('˶', "Modifier Letter Middle Double Acute Accent", '"'),
    ('˵', "Modifier Letter Middle Double Grave Accent", '"'),
    ('ˮ', "Modifier Letter Double Apostrophe", '"'),
    ('ײ', "Hebrew Ligature Yiddish Double Yod", '"'),
    ('❞', "Heavy Double Comma Quotation Mark Ornament", '"'),
    ('❝', "Heavy Double Turned Comma Quotation Mark Ornament", '"'),
    ('［', "Fullwidth Left Square Bracket", '('),
    ('❨', "Medium Left Parenthesis Ornament", '('),
    ('❲', "Light Left Tortoise Shell Bracket Ornament", '('),
    ('〔', "Left Tortoise Shell Bracket", '('),
    ('﴾', "Ornate Left Parenthesis", '('),
    ('］', "Fullwidth Right Square Bracket", ')'),
    ('❩', "Medium Right Parenthesis Ornament", ')'),
    ('❳', "Light Right Tortoise Shell Bracket Ornament", ')'),
    ('〕', "Right Tortoise Shell Bracket", ')'),
    ('﴿', "Ornate Right Parenthesis", ')'),
    ('❴', "Medium Left Curly Bracket Ornament", '{'),
    ('❵', "Medium Right Curly Bracket Ornament", '}'),
    ('⁎', "Low Asterisk", '*'),
    ('٭', "Arabic Five Pointed Star", '*'),
    ('∗', "Asterisk Operator", '*'),
    ('᜵', "Philippine Single Punctuation", '/'),
    ('⁁', "Caret Insertion Point", '/'),
    ('∕', "Division Slash", '/'),
    ('⁄', "Fraction Slash", '/'),
    ('╱', "Box Drawings Light Diagonal Upper Right To Lower Left", '/'),
    ('⟋', "Mathematical Rising Diagonal", '/'),
    ('⧸', "Big Solidus", '/'),
    ('㇓', "Cjk Stroke Sp", '/'),
    ('〳', "Vertical Kana Repeat Mark Upper Half", '/'),
    ('丿', "Cjk Unified Ideograph-4E3F", '/'),
    ('⼃', "Kangxi Radical Slash", '/'),
    ('＼', "Fullwidth Reverse Solidus", '\\'),
    ('﹨', "Small Reverse Solidus", '\\'),
    ('∖', "Set Minus", '\\'),
    ('⟍', "Mathematical Falling Diagonal", '\\'),
    ('⧵', "Reverse Solidus Operator", '\\'),
    ('⧹', "Big Reverse Solidus", '\\'),
    ('㇔', "Cjk Stroke D", '\\'),
    ('丶', "Cjk Unified Ideograph-4E36", '\\'),
    ('⼂', "Kangxi Radical Dot", '\\'),
    ('ꝸ', "Latin Small Letter Um", '&'),
    ('﬩', "Hebrew Letter Alternative Plus Sign", '+'),
    ('‹', "Single Left-Pointing Angle Quotation Mark", '<'),
    ('❮', "Heavy Left-Pointing Angle Quotation Mark Ornament", '<'),
    ('˂', "Modifier Letter Left Arrowhead", '<'),
    ('꓿', "Lisu Punctuation Full Stop", '='),
    ('›', "Single Right-Pointing Angle Quotation Mark", '>'),
    ('❯', "Heavy Right-Pointing Angle Quotation Mark Ornament", '>'),
    ('˃', "Modifier Letter Right Arrowhead", '>'),
    ('Ⲻ', "Coptic Capital Letter Dialect-P Ni", '-'),
    ('Ɂ', "Latin Capital Letter Glottal Stop", '?'),
    ('Ⳇ', "Coptic Capital Letter Old Coptic Esh", '/'), ];

const ASCII_ARRAY: &'static [(char, &'static str)] = &[
    ('_', "Underscore"),
    ('-', "Minus/Hyphen"),
    (',', "Comma"),
    (';', "Semicolon"),
    (':', "Colon"),
    ('!', "Exclamation Mark"),
    ('?', "Question Mark"),
    ('.', "Period"),
    ('\'', "Single Quote"),
    ('"', "Quotation Mark"),
    ('(', "Left Parenthesis"),
    (')', "Right Parenthesis"),
    ('{', "Left Curly Brace"),
    ('}', "Right Curly Brace"),
    ('*', "Asterisk"),
    ('/', "Slash"),
    ('\\', "Backslash"),
    ('&', "Ampersand"),
    ('+', "Plus Sign"),
    ('<', "Less-Than Sign"),
    ('=', "Equals Sign"),
    ('>', "Greater-Than Sign"), ];

pub fn check_for_substitution(reader: &StringReader, ch: char) {
    UNICODE_ARRAY
    .iter()
    .find(|&&(c, _, _)| c == ch)
    .map(|&(_, u_name, ascii_char)| {
        let span = make_span(reader.last_pos, reader.pos);
        match ASCII_ARRAY.iter().find(|&&(c, _)| c == ascii_char) {
            Some(&(ascii_char, ascii_name)) => {
                let msg =
                    format!("unicode character '{}' ({}) looks much like '{}' ({}), but it's not",
                            ch, u_name, ascii_char, ascii_name);
                reader.help_span(span, &msg);
            },
            None => {
                reader
                .span_diagnostic
                .span_bug_no_panic(span,
                                   &format!("substitution character not found for '{}'", ch));
            }
        }
    });
}
<|endoftext|>


<commit_before>// Copyright 2016 Jeremy Mason
//
// Licensed under the MIT license <LICENSE or
// http://opensource.org/licenses/MIT>. This file may not be copied, modified,
// or distributed except according to those terms.

//! The only purpose of this crate is to relegate the functions generated by
//! the encode!, decode!, encode_simd! and decode_simd! syntax extensions of
//! pfor_macros to a separate compilation unit. More extensive documentation is
//! given in the pfor_macros crate.

#![feature(plugin)]
#![plugin(pfor_macros)]

extern crate simd;

encode!(u8, 8, 16);
decode!(u8, 8, 16);
encode!(u16, 16, 16);
decode!(u16, 16, 16);
encode!(u32, 32, 16);
decode!(u32, 32, 16);
encode!(u64, 64, 16);
decode!(u64, 64, 16);

encode_simd!(u8, 8, simd::u8x16);
decode_simd!(u8, 8, simd::u8x16);
encode_simd!(u16, 16, simd::u16x8);
decode_simd!(u16, 16, simd::u16x8);
encode_simd!(u32, 32, simd::u32x4);
decode_simd!(u32, 32, simd::u32x4);
encode_simd!(u64, 64, simd::x86::sse2::u64x2);
decode_simd!(u64, 64, simd::x86::sse2::u64x2);
<commit_msg>Cleaned up pfor_codec<commit_after>// Copyright 2016 Jeremy Mason
//
// Licensed under the MIT license <LICENSE or
// http://opensource.org/licenses/MIT>. This file may not be copied, modified,
// or distributed except according to those terms.

//! The only purpose of this crate is to relegate the functions generated by
//! the encode!, decode!, encode_simd! and decode_simd! syntax extensions of
//! pfor_macros to a separate compilation unit. More extensive documentation is
//! given in the pfor_macros crate.

#![feature(plugin)]
#![plugin(pfor_macros)]

extern crate simd;

macro_rules! codec {
  ($(($ty: expr, $width: expr, $step: expr, $simd: path))*) => ($(
    encode!($ty, $width, $step);
    decode!($ty, $width, $step);
    encode_simd!($ty, $width, $simd);
    decode_simd!($ty, $width, $simd);
  )*)
}

codec!{
  (u8, 8, 16, simd::u8x16)
  (u16, 16, 16, simd::u16x8)
  (u32, 32, 16, simd::u32x4)
  (u64, 64, 16, simd::x86::sse2::u64x2)
}
<|endoftext|>


<commit_before>pub fn verse(index: usize) -> String {
    let mut first = String::new();
    let mut second = String::new();
    let third: &str;
    let mut last = String::new();

    match index {
        2 => {
            first.push_str("2 bottles");
            second = first.clone();
            third = "Take one down and pass it around, ";
            last.push_str("1 bottle of");
        }
        1 => {
            first.push_str("1 bottle");
            second = first.clone();
            third = "Take it down and pass it around, ";
            last.push_str("no more bottles of");
        }
        0 => {
            first.push_str("No more bottles");
            second.push_str("no more bottles");
            third = "Go to the store and buy some more, ";
            last.push_str("99 bottles of");
        }
        _ => {
            first = format!("{} bottles", index);
            second = first.clone();
            third = "Take one down and pass it around, ";
            last = format!("{} bottles of", index - 1);
        }
    };

    format!(
        "{} of beer on the wall, {} of beer.\n{}{} beer on the wall.\n",
        first,
        second,
        third,
        last
    )
}

pub fn sing(upper: usize, lower: usize) -> String {
    let mut result = String::new();

    for i in (lower..upper + 1).rev() {
        result = result + &verse(i);

        result.push('\n');
    }

    result.pop();

    result
}
<commit_msg>Improve the code<commit_after>pub fn verse(index: usize) -> String {
    match index {
        2 => {
            "2 bottles of beer on the wall, 2 bottles of beer.\n\
           Take one down and pass it around, 1 bottle of beer \
           on the wall.\n"
                .to_string()
        }
        1 => {
            "1 bottle of beer on the wall, 1 bottle of beer.\n\
           Take it down and pass it around, no more bottles of beer \
           on the wall.\n"
                .to_string()
        }
        0 => {
            "No more bottles of beer on the wall, no more bottles of beer.\n\
            Go to the store and buy some more, 99 bottles of beer \
            on the wall.\n"
                .to_string()
        }
        _ => {
            format!(
                "{} bottles of beer on the wall, {} bottles of beer.\n\
                Take one down and pass it around, {} bottles of beer \
                on the wall.\n",
                index,
                index,
                index - 1
            )
        }
    }
}

pub fn sing(upper: usize, lower: usize) -> String {
    (lower..upper + 1)
        .rev()
        .map(verse)
        .collect::<Vec<_>>()
        .join("\n")
}
<|endoftext|>


<commit_before>use clamp::{Clamped, ClampedRange};
use math::{Clamp, FPoint3, FromSpace, UPoint3};
use super::space::Axis;

/// Defines the bounds for `Offset` values.
pub struct OffsetRange;

impl ClampedRange<u8> for OffsetRange {
    fn max_value() -> u8 {
        0x0F
    }

    fn min_value() -> u8 {
        0x00
    }
}

/// Edge offset.
///
/// This is the deformation of an `Edge` in a particular direction as an
/// absolute coordinate. It is used to define the endpoints of an `Edge`.
///
/// `Offset` is clamped to the open range `[0, 15]`. This is the same range that
/// the endpoints of an `Edge` are allowed to span.
pub type Offset = Clamped<u8, OffsetRange>;

/// Edge of a cube's `Geometry`.
///
/// `Edge`s define the endpoints of a cube's points along a particular axis as
/// front and back. For each axis, there are four edges describing the
/// coordinates of all eight points along that axis. The front and back of an
/// `Edge` are described by an `Offset` and affect opposing faces of a cube.
///
/// The front and back of an `Edge` cannot cross, but may intersect. These
/// values are packed into the upper and lower 4-bit halves of an `u8`.
#[derive(Copy, Clone)]
pub struct Edge(u8);

impl Edge {
    /// Constructs a new `Edge` that spans the full width (no deformation).
    fn full() -> Self {
        Edge(Offset::max_inner_value())
    }

    /// Constructs a new `Edge` that converges at a given `Offset`.
    fn converged(offset: Offset) -> Self {
        let offset = offset.to_inner();
        Edge((offset << 4) | offset)
    }

    /// Sets the offset of the front of the `Edge`. If the front would cross the
    /// back, it will be clamped such that it intersects the back.
    pub fn set_front(&mut self, offset: Offset) {
        let offset = offset.clamp(Offset::min_value(), self.back()).to_inner();
        self.0 = (offset << 4) | self.back().to_inner();
    }

    /// Sets the `Offset` of the back of the `Edge`. If the back would cross the
    /// front, it will be clamped such that it intersects the front.
    pub fn set_back(&mut self, offset: Offset) {
        let offset = offset.clamp(self.front(), Offset::max_value()).to_inner();
        self.0 = (self.front().to_inner() << 4) | offset;
    }

    /// Gets the `Offset` of the front of the `Edge`.
    pub fn front(&self) -> Offset {
        Offset::from((self.0 & 0xF0) >> 4)
    }

    /// Gets the `Offset` of the back of the `Edge`.
    pub fn back(&self) -> Offset {
        Offset::from(self.0 & 0x0F)
    }

    /// Gets the length of the `Edge`.
    pub fn length(&self) -> Offset {
        self.back() - self.front()
    }

    fn front_unit_transform(&self) -> f32 {
        let min = Offset::min_inner_value();
        ((self.front().to_inner() - min) as f32) / ((Offset::max_inner_value() - min) as f32)
    }

    fn back_unit_transform(&self) -> f32 {
        let len = Offset::max_inner_value() - Offset::min_inner_value();
        -((len - (self.back().to_inner() - Offset::min_inner_value())) as f32) / (len as f32)
    }
}

#[derive(Copy, Clone)]
pub struct Geometry([[Edge; 4]; 3]);

impl Geometry {
    pub fn full() -> Self {
        Geometry([[Edge::full(); 4]; 3])
    }

    pub fn empty() -> Self {
        Geometry([[Edge::converged(Offset::from(0)); 4]; 3])
    }

    pub fn edges(&self, axis: Axis) -> &[Edge; 4] {
        &self.0[axis as usize]
    }

    pub fn edges_mut(&mut self, axis: Axis) -> &mut [Edge; 4] {
        &mut self.0[axis as usize]
    }

    pub fn is_empty(&self) -> bool {
        self.0.iter().any(|axis| axis.iter().all(|edge| edge.length() == 0))
    }

    pub fn map_unit_cube_point(&self, unit: &UPoint3) -> FPoint3 {
        let mut point = FPoint3::from_space(*unit);
        for axis in Axis::range() {
            let edge = &self.0[axis][index_at_axis(axis, unit)];
            point[axis] += if unit[axis] == 0 {
                edge.front_unit_transform()
            }
            else {
                edge.back_unit_transform()
            };
        }
        point
    }
}

/// Gets the index of an `Edge` in a face (a collection of four `Edge`s along a
/// particular axis).
fn index_at_axis(axis: usize, unit: &UPoint3) -> usize {
    let p = if axis == 0 { 1 } else { 0 };
    let q = if axis == 2 { 1 } else { 2 };
    (unit[p] | (unit[q] << 1)) as usize
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>Use `FScalar` in `Edge` transforms.<commit_after>use clamp::{Clamped, ClampedRange};
use math::{Clamp, FPoint3, FromSpace, FScalar, UPoint3};
use super::space::Axis;

/// Defines the bounds for `Offset` values.
pub struct OffsetRange;

impl ClampedRange<u8> for OffsetRange {
    fn max_value() -> u8 {
        0x0F
    }

    fn min_value() -> u8 {
        0x00
    }
}

/// Edge offset.
///
/// This is the deformation of an `Edge` in a particular direction as an
/// absolute coordinate. It is used to define the endpoints of an `Edge`.
///
/// `Offset` is clamped to the open range `[0, 15]`. This is the same range that
/// the endpoints of an `Edge` are allowed to span.
pub type Offset = Clamped<u8, OffsetRange>;

/// Edge of a cube's `Geometry`.
///
/// `Edge`s define the endpoints of a cube's points along a particular axis as
/// front and back. For each axis, there are four edges describing the
/// coordinates of all eight points along that axis. The front and back of an
/// `Edge` are described by an `Offset` and affect opposing faces of a cube.
///
/// The front and back of an `Edge` cannot cross, but may intersect. These
/// values are packed into the upper and lower 4-bit halves of an `u8`.
#[derive(Copy, Clone)]
pub struct Edge(u8);

impl Edge {
    /// Constructs a new `Edge` that spans the full width (no deformation).
    fn full() -> Self {
        Edge(Offset::max_inner_value())
    }

    /// Constructs a new `Edge` that converges at a given `Offset`.
    fn converged(offset: Offset) -> Self {
        let offset = offset.to_inner();
        Edge((offset << 4) | offset)
    }

    /// Sets the offset of the front of the `Edge`. If the front would cross the
    /// back, it will be clamped such that it intersects the back.
    pub fn set_front(&mut self, offset: Offset) {
        let offset = offset.clamp(Offset::min_value(), self.back()).to_inner();
        self.0 = (offset << 4) | self.back().to_inner();
    }

    /// Sets the `Offset` of the back of the `Edge`. If the back would cross the
    /// front, it will be clamped such that it intersects the front.
    pub fn set_back(&mut self, offset: Offset) {
        let offset = offset.clamp(self.front(), Offset::max_value()).to_inner();
        self.0 = (self.front().to_inner() << 4) | offset;
    }

    /// Gets the `Offset` of the front of the `Edge`.
    pub fn front(&self) -> Offset {
        Offset::from((self.0 & 0xF0) >> 4)
    }

    /// Gets the `Offset` of the back of the `Edge`.
    pub fn back(&self) -> Offset {
        Offset::from(self.0 & 0x0F)
    }

    /// Gets the length of the `Edge`.
    pub fn length(&self) -> Offset {
        self.back() - self.front()
    }

    fn front_unit_transform(&self) -> FScalar {
        let min = Offset::min_inner_value();
        let n = (self.front().to_inner() - min) as FScalar;
        let d = (Offset::max_inner_value() - min) as FScalar;
        n / d
    }

    fn back_unit_transform(&self) -> FScalar {
        let min = Offset::min_inner_value();
        let span = Offset::max_inner_value() - min;
        let n = -((span - (self.back().to_inner() - min)) as FScalar);
        let d = span as FScalar;
        n / d
    }
}

#[derive(Copy, Clone)]
pub struct Geometry([[Edge; 4]; 3]);

impl Geometry {
    pub fn full() -> Self {
        Geometry([[Edge::full(); 4]; 3])
    }

    pub fn empty() -> Self {
        Geometry([[Edge::converged(Offset::from(0)); 4]; 3])
    }

    pub fn edges(&self, axis: Axis) -> &[Edge; 4] {
        &self.0[axis as usize]
    }

    pub fn edges_mut(&mut self, axis: Axis) -> &mut [Edge; 4] {
        &mut self.0[axis as usize]
    }

    pub fn is_empty(&self) -> bool {
        self.0.iter().any(|axis| axis.iter().all(|edge| edge.length() == 0))
    }

    pub fn map_unit_cube_point(&self, unit: &UPoint3) -> FPoint3 {
        let mut point = FPoint3::from_space(*unit);
        for axis in Axis::range() {
            let edge = &self.0[axis][index_at_axis(axis, unit)];
            point[axis] += if unit[axis] == 0 {
                edge.front_unit_transform()
            }
            else {
                edge.back_unit_transform()
            };
        }
        point
    }
}

/// Gets the index of an `Edge` in a face (a collection of four `Edge`s along a
/// particular axis).
fn index_at_axis(axis: usize, unit: &UPoint3) -> usize {
    let p = if axis == 0 { 1 } else { 0 };
    let q = if axis == 2 { 1 } else { 2 };
    (unit[p] | (unit[q] << 1)) as usize
}

#[cfg(test)]
mod tests {
    use super::*;
}
<|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::*;
use services::pool::PoolWorker;

pub const CATCHUP_ROUND_TIMEOUT: i64 = 50;

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

#[derive(Debug)]
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, Option<Vec<String>>)>>,
    pub pool_name: String,
}

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(),
            pool_name: "".to_string(),
        }
    }
}

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, None));
                self.check_nodes_responses_on_status()?
            }
            Message::ConsistencyProof(cons_proof) => {
                self.nodes_votes[src_ind] = Some((cons_proof.newMerkleRoot, cons_proof.seqNoEnd, Some(cons_proof.hashes)));
                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 votes: HashMap<(String, usize, Option<Vec<String>>), usize> = self.nodes_votes.iter().cloned()
            .filter_map(|e| e)
            .fold(HashMap::new(), |mut acc, vote| {
                let cnt = acc.get(&vote).unwrap_or(&0) + 1;
                acc.insert(vote, cnt);
                acc
            });

        if let Some((most_popular_vote, votes_cnt)) = votes.iter().max_by_key(|entry| entry.1) {
            if *votes_cnt == self.nodes.len() - self.f {
                return match self._try_to_catch_up(most_popular_vote) {
                    //WARNING: If we receive invalid cache then it means that cache was cleared. We need to rebuild merkle tree and try to catch up once again
                    Err(PoolError::InvalidCacheCleared) => {
                        self.merkle_tree = PoolWorker::restore_merkle_tree_from_pool_name(&self.pool_name)?;
                        self._try_to_catch_up(most_popular_vote)
                    }
                    a => a
                }
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn _try_to_catch_up(&mut self, most_popular_vote: &(String, usize, Option<Vec<String>>)) -> Result<CatchupProgress, PoolError> {
        let &(ref target_mt_root, target_mt_size, ref hashes) = 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 {
                PoolWorker::drop_saved_txns(&self.pool_name)?;
                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()))?;
            match hashes {
                &None => (),
                &Some(ref hashes) => {
                    match CatchupHandler::check_cons_proofs(&self.merkle_tree, hashes, &self.target_mt_root, self.target_mt_size) {
                        Ok(_) => (),
                        Err(err) => {
                            PoolWorker::drop_saved_txns(&self.pool_name)?;
                            return Err(PoolError::from(err));
                        }
                    }
                }
            };
            return Ok(CatchupProgress::ShouldBeStarted);
        } else {
            PoolWorker::drop_saved_txns(&self.pool_name)?;
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Local merkle tree greater than mt from ledger".to_string())));
        }
    }

    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();
                let mut vec_to_dump: Vec<Vec<u8>> = vec![];
                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.clone())?;
                        vec_to_dump.push(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));
                }

                PoolWorker::dump_new_txns(&self.pool_name, &vec_to_dump)?;

                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(format!("Can't send ACK cmd: {:?}", err))))
    }

    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>Cleanup code<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::*;
use services::pool::PoolWorker;

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, Option<Vec<String>>)>>,
    pub pool_name: String,
}

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(),
            pool_name: "".to_string(),
        }
    }
}

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, None));
                self.check_nodes_responses_on_status()?
            }
            Message::ConsistencyProof(cons_proof) => {
                self.nodes_votes[src_ind] = Some((cons_proof.newMerkleRoot, cons_proof.seqNoEnd, Some(cons_proof.hashes)));
                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 votes: HashMap<(String, usize, Option<Vec<String>>), usize> = self.nodes_votes.iter().cloned()
            .filter_map(|e| e)
            .fold(HashMap::new(), |mut acc, vote| {
                let cnt = acc.get(&vote).unwrap_or(&0) + 1;
                acc.insert(vote, cnt);
                acc
            });

        if let Some((most_popular_vote, votes_cnt)) = votes.iter().max_by_key(|entry| entry.1) {
            if *votes_cnt == self.nodes.len() - self.f {
                return match self._try_to_catch_up(most_popular_vote) {
                    //WARNING: If we receive invalid cache then it means that cache was cleared. We need to rebuild merkle tree and try to catch up once again
                    Err(PoolError::InvalidCacheCleared) => {
                        self.merkle_tree = PoolWorker::restore_merkle_tree_from_pool_name(&self.pool_name)?;
                        self._try_to_catch_up(most_popular_vote)
                    }
                    a => a
                }
            }
        }
        Ok(CatchupProgress::InProgress)
    }

    fn _try_to_catch_up(&mut self, most_popular_vote: &(String, usize, Option<Vec<String>>)) -> Result<CatchupProgress, PoolError> {
        let &(ref target_mt_root, target_mt_size, ref hashes) = 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 {
                PoolWorker::drop_saved_txns(&self.pool_name)?;
                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()))?;
            match hashes {
                &None => (),
                &Some(ref hashes) => {
                    match CatchupHandler::check_cons_proofs(&self.merkle_tree, hashes, &self.target_mt_root, self.target_mt_size) {
                        Ok(_) => (),
                        Err(err) => {
                            PoolWorker::drop_saved_txns(&self.pool_name)?;
                            return Err(PoolError::from(err));
                        }
                    }
                }
            };
            return Ok(CatchupProgress::ShouldBeStarted);
        } else {
            PoolWorker::drop_saved_txns(&self.pool_name)?;
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Local merkle tree greater than mt from ledger".to_string())));
        }
    }

    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();
                let mut vec_to_dump: Vec<Vec<u8>> = vec![];
                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.clone())?;
                        vec_to_dump.push(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));
                }

                PoolWorker::dump_new_txns(&self.pool_name, &vec_to_dump)?;

                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(format!("Can't send ACK cmd: {:?}", err))))
    }

    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|>