<commit_before>use crate::cpp_data::{CppItem, CppPath, CppPathItem};
use crate::cpp_function::{CppFunction, CppFunctionArgument, CppOperator};
use crate::cpp_type::CppType;
use crate::processor::ProcessorData;
use log::{debug, trace};
use ritual_common::errors::{bail, err_msg, Result};

/// Returns true if `type1` is a known template instantiation.
fn check_template_type(data: &ProcessorData<'_>, type1: &CppType) -> Result<()> {
    match &type1 {
        CppType::Class(path) => {
            if let Some(template_arguments) = &path.last().template_arguments {
                let is_available = data
                    .db
                    .all_cpp_items()
                    .filter_map(|i| i.item.as_type_ref())
                    .any(|inst| &inst.path == path);
                if !is_available {
                    bail!("type is not available: {:?}", type1);
                }
                for arg in template_arguments {
                    check_template_type(data, arg)?;
                }
            }
        }
        CppType::PointerLike { ref target, .. } => {
            check_template_type(data, target)?;
        }
        _ => {}
    }
    Ok(())
}

/// Tries to apply each of `template_instantiations` to `method`.
/// Only types at the specified `nested_level` are replaced.
/// Returns `Err` if any of `template_instantiations` is incompatible
/// with the method.
fn apply_instantiation_to_method(
    method: &CppFunction,
    nested_level1: usize,
    template_instantiation: &CppPath,
) -> Result<CppFunction> {
    let mut new_method = method.clone();

    let inst_args = template_instantiation
        .last()
        .template_arguments
        .as_ref()
        .ok_or_else(|| err_msg("template instantiation must have template arguments"))?;

    new_method.arguments.clear();
    for arg in &method.arguments {
        new_method.arguments.push(CppFunctionArgument {
            name: arg.name.clone(),
            has_default_value: arg.has_default_value,
            argument_type: arg.argument_type.instantiate(nested_level1, inst_args)?,
        });
    }
    new_method.return_type = method.return_type.instantiate(nested_level1, inst_args)?;

    new_method.path = new_method.path.instantiate(nested_level1, inst_args)?;
    let mut conversion_type = None;
    if let Some(operator) = &mut new_method.operator {
        if let CppOperator::Conversion(cpp_type) = operator {
            let r = cpp_type.instantiate(nested_level1, inst_args)?;
            *cpp_type = r.clone();
            conversion_type = Some(r);
        }
    }
    if new_method
        .all_involved_types()
        .iter()
        .any(CppType::is_or_contains_template_parameter)
    {
        bail!(
            "extra template parameters left: {}",
            new_method.short_text()
        );
    } else {
        if let Some(conversion_type) = conversion_type {
            *new_method.path.last_mut() = CppPathItem {
                name: format!("operator {}", conversion_type.to_cpp_code(None)?),
                template_arguments: None,
            };
        }
        trace!("success: {}", new_method.short_text());
        Ok(new_method)
    }
}

// TODO: instantiations of QObject::findChild and QObject::findChildren should be available

/// Generates methods as template instantiations of
/// methods of existing template classes and existing template methods.
pub fn instantiate_templates(data: &mut ProcessorData<'_>) -> Result<()> {
    let mut new_methods = Vec::new();
    for item in data.db.all_cpp_items() {
        let function = if let Some(f) = item.item.as_function_ref() {
            f
        } else {
            continue;
        };

        for type1 in function.all_involved_types() {
            let path = match &type1 {
                CppType::Class(class_type) => class_type,
                CppType::PointerLike { target, .. } => match &**target {
                    CppType::Class(class_type) => class_type,
                    _ => continue,
                },
                _ => continue,
            };
            let template_arguments = if let Some(args) = &path.last().template_arguments {
                args
            } else {
                continue;
            };
            assert!(!template_arguments.is_empty());
            if !template_arguments.iter().all(|t| t.is_template_parameter()) {
                continue;
            }
            for type1 in data
                .db
                .cpp_items()
                .filter_map(|item| item.item.as_type_ref())
            {
                let is_suitable = type1.path.parent().ok() == path.parent().ok()
                    && type1.path.last().name == path.last().name
                    && type1
                        .path
                        .last()
                        .template_arguments
                        .as_ref()
                        .map_or(false, |args| {
                            !args.iter().all(CppType::is_or_contains_template_parameter)
                        });

                if !is_suitable {
                    continue;
                }
                let nested_level = if let CppType::TemplateParameter { nested_level, .. } =
                    template_arguments[0]
                {
                    nested_level
                } else {
                    bail!("only template parameters can be here");
                };
                trace!("method: {}", function.short_text());
                trace!(
                    "found template instantiation: {}",
                    type1.path.to_cpp_pseudo_code()
                );
                match apply_instantiation_to_method(function, nested_level, &type1.path) {
                    Ok(method) => {
                        let mut ok = true;
                        for type1 in method.all_involved_types() {
                            match check_template_type(&data, &type1) {
                                Ok(_) => {}
                                Err(msg) => {
                                    ok = false;
                                    trace!("method is not accepted: {}", method.short_text());
                                    trace!("  {}", msg);
                                }
                            }
                        }
                        if ok {
                            new_methods.push((method, item.source_id.clone()));
                        }
                    }
                    Err(msg) => trace!("failed: {}", msg),
                }
            }
        }
    }
    for (item, source_id) in new_methods {
        data.db.add_cpp_item(source_id, CppItem::Function(item))?;
    }
    Ok(())
}

/// Searches for template instantiations in this library's API,
/// excluding results that were already processed in dependencies.
pub fn find_template_instantiations(data: &mut ProcessorData<'_>) -> Result<()> {
    fn check_type(type1: &CppType, data: &ProcessorData<'_>, result: &mut Vec<CppPath>) {
        match &type1 {
            CppType::Class(path) => {
                if let Some(template_arguments) = &path.last().template_arguments {
                    if !template_arguments
                        .iter()
                        .any(CppType::is_or_contains_template_parameter)
                    {
                        let is_in_database = data
                            .db
                            .all_cpp_items()
                            .filter_map(|item| item.item.as_type_ref())
                            .any(|i| &i.path == path);
                        if !is_in_database {
                            let is_in_result = result.iter().any(|x| x == path);
                            if !is_in_result {
                                result.push(path.clone());
                            }
                        }
                    }
                    for arg in template_arguments {
                        check_type(arg, &data, result);
                    }
                }
            }
            CppType::PointerLike { target, .. } => check_type(target, data, result),
            _ => {}
        }
    }
    let mut result = Vec::new();
    for item in data.db.cpp_items() {
        for type1 in item.item.all_involved_types() {
            check_type(&type1, &data, &mut result);
        }
    }
    for item in result {
        let original_type = data
            .db
            .all_cpp_items()
            .filter_map(|x| x.item.as_type_ref())
            .find(|t| {
                t.path.parent().ok() == item.parent().ok()
                    && t.path.last().name == item.last().name
                    && t.path
                        .last()
                        .template_arguments
                        .as_ref()
                        .map_or(false, |args| {
                            args.iter().all(CppType::is_template_parameter)
                        })
            });
        if let Some(original_type) = original_type {
            let mut new_type = original_type.clone();
            new_type.path = item;
            data.db.add_cpp_item(
                None, // TODO: what is the source ffi item?
                CppItem::Type(new_type),
            )?;
        } else {
            debug!(
                "original type not found for instantiation: {}",
                item.to_cpp_pseudo_code()
            );
        }
    }
    Ok(())
}
<commit_msg>omit generic args in instantiated methods<commit_after>use crate::cpp_data::{CppItem, CppPath, CppPathItem};
use crate::cpp_function::{CppFunction, CppFunctionArgument, CppOperator};
use crate::cpp_type::CppType;
use crate::processor::ProcessorData;
use log::{debug, trace};
use ritual_common::errors::{bail, err_msg, Result};

/// Returns true if `type1` is a known template instantiation.
fn check_template_type(data: &ProcessorData<'_>, type1: &CppType) -> Result<()> {
    match &type1 {
        CppType::Class(path) => {
            if let Some(template_arguments) = &path.last().template_arguments {
                let is_available = data
                    .db
                    .all_cpp_items()
                    .filter_map(|i| i.item.as_type_ref())
                    .any(|inst| &inst.path == path);
                if !is_available {
                    bail!("type is not available: {:?}", type1);
                }
                for arg in template_arguments {
                    check_template_type(data, arg)?;
                }
            }
        }
        CppType::PointerLike { ref target, .. } => {
            check_template_type(data, target)?;
        }
        _ => {}
    }
    Ok(())
}

/// Tries to apply each of `template_instantiations` to `method`.
/// Only types at the specified `nested_level` are replaced.
/// Returns `Err` if any of `template_instantiations` is incompatible
/// with the method.
fn apply_instantiation_to_method(
    method: &CppFunction,
    nested_level1: usize,
    template_instantiation: &CppPath,
) -> Result<CppFunction> {
    let mut new_method = method.clone();

    let inst_args = template_instantiation
        .last()
        .template_arguments
        .as_ref()
        .ok_or_else(|| err_msg("template instantiation must have template arguments"))?;

    new_method.arguments.clear();
    for arg in &method.arguments {
        new_method.arguments.push(CppFunctionArgument {
            name: arg.name.clone(),
            has_default_value: arg.has_default_value,
            argument_type: arg.argument_type.instantiate(nested_level1, inst_args)?,
        });
    }
    new_method.return_type = method.return_type.instantiate(nested_level1, inst_args)?;

    new_method.path = new_method.path.instantiate(nested_level1, inst_args)?;
    if let Some(args) = &new_method.path.last().template_arguments {
        if args
            .iter()
            .any(|arg| arg.is_or_contains_template_parameter())
        {
            bail!(
                "extra template parameters left: {}",
                new_method.short_text()
            );
        }
        // explicitly specifying template arguments sometimes causes compiler errors,
        // so we prefer to get them inferred
        new_method.path.last_mut().template_arguments = None;
    }

    let mut conversion_type = None;
    if let Some(operator) = &mut new_method.operator {
        if let CppOperator::Conversion(cpp_type) = operator {
            let r = cpp_type.instantiate(nested_level1, inst_args)?;
            *cpp_type = r.clone();
            conversion_type = Some(r);
        }
    }
    if new_method
        .all_involved_types()
        .iter()
        .any(CppType::is_or_contains_template_parameter)
    {
        bail!(
            "extra template parameters left: {}",
            new_method.short_text()
        );
    } else {
        if let Some(conversion_type) = conversion_type {
            *new_method.path.last_mut() = CppPathItem {
                name: format!("operator {}", conversion_type.to_cpp_code(None)?),
                template_arguments: None,
            };
        }
        trace!("success: {}", new_method.short_text());
        Ok(new_method)
    }
}

// TODO: instantiations of QObject::findChild and QObject::findChildren should be available

/// Generates methods as template instantiations of
/// methods of existing template classes and existing template methods.
pub fn instantiate_templates(data: &mut ProcessorData<'_>) -> Result<()> {
    let mut new_methods = Vec::new();
    for item in data.db.all_cpp_items() {
        let function = if let Some(f) = item.item.as_function_ref() {
            f
        } else {
            continue;
        };

        for type1 in function.all_involved_types() {
            let path = match &type1 {
                CppType::Class(class_type) => class_type,
                CppType::PointerLike { target, .. } => match &**target {
                    CppType::Class(class_type) => class_type,
                    _ => continue,
                },
                _ => continue,
            };
            let template_arguments = if let Some(args) = &path.last().template_arguments {
                args
            } else {
                continue;
            };
            assert!(!template_arguments.is_empty());
            if !template_arguments.iter().all(|t| t.is_template_parameter()) {
                continue;
            }
            for type1 in data
                .db
                .cpp_items()
                .filter_map(|item| item.item.as_type_ref())
            {
                let is_suitable = type1.path.parent().ok() == path.parent().ok()
                    && type1.path.last().name == path.last().name
                    && type1
                        .path
                        .last()
                        .template_arguments
                        .as_ref()
                        .map_or(false, |args| {
                            !args.iter().all(CppType::is_or_contains_template_parameter)
                        });

                if !is_suitable {
                    continue;
                }
                let nested_level = if let CppType::TemplateParameter { nested_level, .. } =
                    template_arguments[0]
                {
                    nested_level
                } else {
                    bail!("only template parameters can be here");
                };
                trace!("method: {}", function.short_text());
                trace!(
                    "found template instantiation: {}",
                    type1.path.to_cpp_pseudo_code()
                );
                match apply_instantiation_to_method(function, nested_level, &type1.path) {
                    Ok(method) => {
                        let mut ok = true;
                        for type1 in method.all_involved_types() {
                            match check_template_type(&data, &type1) {
                                Ok(_) => {}
                                Err(msg) => {
                                    ok = false;
                                    trace!("method is not accepted: {}", method.short_text());
                                    trace!("  {}", msg);
                                }
                            }
                        }
                        if ok {
                            new_methods.push((method, item.source_id.clone()));
                        }
                    }
                    Err(msg) => trace!("failed: {}", msg),
                }
            }
        }
    }
    for (item, source_id) in new_methods {
        data.db.add_cpp_item(source_id, CppItem::Function(item))?;
    }
    Ok(())
}

/// Searches for template instantiations in this library's API,
/// excluding results that were already processed in dependencies.
pub fn find_template_instantiations(data: &mut ProcessorData<'_>) -> Result<()> {
    fn check_type(type1: &CppType, data: &ProcessorData<'_>, result: &mut Vec<CppPath>) {
        match &type1 {
            CppType::Class(path) => {
                if let Some(template_arguments) = &path.last().template_arguments {
                    if !template_arguments
                        .iter()
                        .any(CppType::is_or_contains_template_parameter)
                    {
                        let is_in_database = data
                            .db
                            .all_cpp_items()
                            .filter_map(|item| item.item.as_type_ref())
                            .any(|i| &i.path == path);
                        if !is_in_database {
                            let is_in_result = result.iter().any(|x| x == path);
                            if !is_in_result {
                                result.push(path.clone());
                            }
                        }
                    }
                    for arg in template_arguments {
                        check_type(arg, &data, result);
                    }
                }
            }
            CppType::PointerLike { target, .. } => check_type(target, data, result),
            _ => {}
        }
    }
    let mut result = Vec::new();
    for item in data.db.cpp_items() {
        for type1 in item.item.all_involved_types() {
            check_type(&type1, &data, &mut result);
        }
    }
    for item in result {
        let original_type = data
            .db
            .all_cpp_items()
            .filter_map(|x| x.item.as_type_ref())
            .find(|t| {
                t.path.parent().ok() == item.parent().ok()
                    && t.path.last().name == item.last().name
                    && t.path
                        .last()
                        .template_arguments
                        .as_ref()
                        .map_or(false, |args| {
                            args.iter().all(CppType::is_template_parameter)
                        })
            });
        if let Some(original_type) = original_type {
            let mut new_type = original_type.clone();
            new_type.path = item;
            data.db.add_cpp_item(
                None, // TODO: what is the source ffi item?
                CppItem::Type(new_type),
            )?;
        } else {
            debug!(
                "original type not found for instantiation: {}",
                item.to_cpp_pseudo_code()
            );
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>macro_rules! assert_special {
    ($left:expr, $right:expr) => ({
        match (&$left, &$right) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion_failed: `(left == right)`\
                            (left: `{:?}` right: `{:?}`)",
                    left_val, right_val)
                }

                true
            }
        }
    });
}

macro_rules! vector {
    ($elem:expr, $n:expr) => {
        ::std::vec::from_elem($elem, $n)
    };
    ($($x:expr),*) => {
        <[_]>::into_vec(Box::new([ $( $x ), *]))
    };
    ($($x:expr),+ ,) => {
        vector![$($x), *]
    }
}

// Pattern language of regular expressions:

// $( ... )* Match 0 or more times with no separator

// $( ... ),* Match 0 or more times, separated by commas

// $( ... );* Match 0 or more times, separated by semicolons

// $( ... )+ Match 1 or more times with no separator

// $( ... ),+ Match 1 or more times, separated by commas

// $( ... );+ Match 1 or more times, separated by semicolons

fn main() {
    let val = assert_special!("hello", "hello");
    println!("{:?}", val);

    let some_vector = vector![10, 1];
    println!("{:?}", some_vector);
}
<commit_msg>small note about trailing comma trick in macro<commit_after>macro_rules! assert_special {
    ($left:expr, $right:expr) => ({
        match (&$left, &$right) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion_failed: `(left == right)`\
                            (left: `{:?}` right: `{:?}`)",
                    left_val, right_val)
                }

                true
            }
        }
    });
}

macro_rules! vector {
    ($elem:expr, $n:expr) => {
        ::std::vec::from_elem($elem, $n)
    };
    ($($x:expr),*) => {
        <[_]>::into_vec(Box::new([ $( $x ), *]))
    };
    // adds support for trailing comma
    ($($x:expr),+ ,) => {
        vector![$($x), *]
    }
}

// Pattern language of regular expressions:

// $( ... )* Match 0 or more times with no separator

// $( ... ),* Match 0 or more times, separated by commas

// $( ... );* Match 0 or more times, separated by semicolons

// $( ... )+ Match 1 or more times with no separator

// $( ... ),+ Match 1 or more times, separated by commas

// $( ... );+ Match 1 or more times, separated by semicolons

fn main() {
    let val = assert_special!("hello", "hello");
    println!("{:?}", val);

    let some_vector = vector![10, 1];
    println!("{:?}", some_vector);
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented

    // finding all the descriptors
    let mut descriptors = Vec::new();
    struct Descriptor {
        name: String,
        desc_ty: String,
        bind_ty: String,
        bind_start: String,
        bind_end: String,
        set: u32,
        binding: u32,
    }

    // looping to find all the elements that have the `DescriptorSet` decoration
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationDescriptorSet, ref params } => {
                (target_id, params[0])
            },
            _ => continue
        };

        // find which type is pointed to by this variable
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // name of the variable
        let name = ::name_from_id(doc, variable_id);

        // find the binding point of this descriptor
        let binding = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBinding, ref params } if target_id == variable_id => {
                    Some(params[0])
                },
                _ => None,      // TODO: other types
            }
        }).next().expect("A uniform is missing a binding");

        // find informations about the kind of binding for this descriptor
        let (desc_ty, bind_ty, bind_start, bind_end) = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::UniformBuffer",
                        "::vulkano::buffer::AbstractBuffer",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer { buffer: ",
                        ", offset: 0, size: 128 /* FIXME */ }"
                    ))
                },
                &parse::Instruction::TypeImage { result_id, sampled_type_id, ref dim, arrayed, ms,
                                                 sampled, ref format, ref access, .. }
                                        if result_id == pointed_ty && sampled == Some(true) =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer",      // FIXME:
                        ""
                    ))
                },
                &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                    if result_id == pointed_ty =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer",      // FIXME:
                        ""
                    ))
                },
                _ => None,      // TODO: other types
            }
        }).next().expect(&format!("Couldn't find relevant type for uniform `{}` (maybe unimplemented)", name));

        descriptors.push(Descriptor {
            name: name,
            desc_ty: desc_ty.to_owned(),
            bind_ty: bind_ty.to_owned(),
            bind_start: bind_start.to_owned(),
            bind_end: bind_end.to_owned(),
            set: descriptor_set,
            binding: binding,
        });
    }

    let sets_list = descriptors.iter().map(|d| d.set).collect::<HashSet<u32>>();

    let mut output = String::new();

    // iterate once per set that is defined somewhere
    for set in sets_list.iter() {
        let write_ty = descriptors.iter().filter(|d| d.set == *set)
                                  .map(|d| format!("::std::sync::Arc<{}>", d.bind_ty))
                                  .collect::<Vec<_>>();

        let writes = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                                .map(|(entry, d)| {
                                    let entry = if write_ty.len() == 1 {
                                        "".to_owned()
                                    } else {
                                        format!(".{}", entry)
                                    };

                                    format!("::vulkano::descriptor_set::DescriptorWrite {{
                                                 binding: {binding},
                                                 array_element: 0,
                                                 content: {bind_start} data{entry} {bind_end},
                                             }}", binding = d.binding, bind_start = d.bind_start,
                                                  bind_end = d.bind_end,
                                                  entry = entry)
                                })
                                .collect::<Vec<_>>();

        let descr = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                               .map(|(entry, d)| {
                                   format!("::vulkano::descriptor_set::DescriptorDesc {{
                                                binding: {binding},
                                                ty: {desc_ty},
                                                array_count: 1,
                                                stages: ::vulkano::descriptor_set::ShaderStages::all_graphics(),        // TODO:
                                            }}", binding = d.binding, desc_ty = d.desc_ty)
                               })
                               .collect::<Vec<_>>();

        output.push_str(&format!(r#"
#[derive(Default)]
pub struct Set{set};

unsafe impl ::vulkano::descriptor_set::SetLayout for Set{set} {{
    fn descriptors(&self) -> Vec<::vulkano::descriptor_set::DescriptorDesc> {{
        vec![
            {descr}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutWrite<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        vec![
            {writes}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutInit<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        ::vulkano::descriptor_set::SetLayoutWrite::decode(self, data)
    }}
}}

"#, set = set, write_ty = write_ty.join(","), writes = writes.join(","), descr = descr.join(",")));
    }

    let max_set = sets_list.iter().cloned().max().map(|v| v + 1).unwrap_or(0);

    let sets_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSet<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let sets = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if sets_defs.len() == 1 {
                format!("sets")
            } else {
                format!("sets.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    let layouts_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSetLayout<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let layouts = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if layouts_defs.len() == 1 {
                format!("layouts")
            } else {
                format!("layouts.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    output.push_str(&format!(r#"
#[derive(Default)]
pub struct Layout;

unsafe impl ::vulkano::descriptor_set::Layout for Layout {{
    type DescriptorSets = ({sets_defs});
    type DescriptorSetLayouts = ({layouts_defs});
    type PushConstants = ();

    fn decode_descriptor_set_layouts(&self, layouts: Self::DescriptorSetLayouts)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSetLayout>>
    {{
        vec![
            {layouts}
        ]
    }}

    fn decode_descriptor_sets(&self, sets: Self::DescriptorSets)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSet>>
    {{
        vec![
            {sets}
        ]
    }}
}}
"#, sets_defs = sets_defs.join(","), layouts_defs = layouts_defs.join(","),
    layouts = layouts.join(","), sets = sets.join(",")));

    output
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::Variable { result_type_id, result_id, .. } if result_id == variable => {
                Some(result_type_id)
            },
            _ => None
        }
    }).next().unwrap();

    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypePointer { result_id, type_id, .. } if result_id == var_ty => {
                Some(type_id)
            },
            _ => None
        }
    }).next().unwrap()
}
<commit_msg>Slightly better error message in descriptor set gen<commit_after>use std::collections::HashSet;

use enums;
use parse;

pub fn write_descriptor_sets(doc: &parse::Spirv) -> String {
    // TODO: not implemented

    // finding all the descriptors
    let mut descriptors = Vec::new();
    struct Descriptor {
        name: String,
        desc_ty: String,
        bind_ty: String,
        bind_start: String,
        bind_end: String,
        set: u32,
        binding: u32,
    }

    // looping to find all the elements that have the `DescriptorSet` decoration
    for instruction in doc.instructions.iter() {
        let (variable_id, descriptor_set) = match instruction {
            &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationDescriptorSet, ref params } => {
                (target_id, params[0])
            },
            _ => continue
        };

        // find which type is pointed to by this variable
        let pointed_ty = pointer_variable_ty(doc, variable_id);
        // name of the variable
        let name = ::name_from_id(doc, variable_id);

        // find the binding point of this descriptor
        let binding = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::Decorate { target_id, decoration: enums::Decoration::DecorationBinding, ref params } if target_id == variable_id => {
                    Some(params[0])
                },
                _ => None,      // TODO: other types
            }
        }).next().expect("A uniform is missing a binding");

        // find informations about the kind of binding for this descriptor
        let (desc_ty, bind_ty, bind_start, bind_end) = doc.instructions.iter().filter_map(|i| {
            match i {
                &parse::Instruction::TypeStruct { result_id, .. } if result_id == pointed_ty => {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::UniformBuffer",
                        "::vulkano::buffer::AbstractBuffer",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer { buffer: ",
                        ", offset: 0, size: 128 /* FIXME */ }"
                    ))
                },
                &parse::Instruction::TypeImage { result_id, sampled_type_id, ref dim, arrayed, ms,
                                                 sampled, ref format, ref access, .. }
                                        if result_id == pointed_ty && sampled == Some(true) =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer",      // FIXME:
                        ""
                    ))
                },
                &parse::Instruction::TypeSampledImage { result_id, image_type_id }
                                                                    if result_id == pointed_ty =>
                {
                    Some((
                        "::vulkano::descriptor_set::DescriptorType::SampledImage",
                        "::vulkano::image::AbstractImageView",
                        "::vulkano::descriptor_set::DescriptorBind::UniformBuffer",      // FIXME:
                        ""
                    ))
                },
                _ => None,      // TODO: other types
            }
        }).next().expect(&format!("Couldn't find relevant type for uniform `{}` (type {}, maybe unimplemented)", name, pointed_ty));

        descriptors.push(Descriptor {
            name: name,
            desc_ty: desc_ty.to_owned(),
            bind_ty: bind_ty.to_owned(),
            bind_start: bind_start.to_owned(),
            bind_end: bind_end.to_owned(),
            set: descriptor_set,
            binding: binding,
        });
    }

    let sets_list = descriptors.iter().map(|d| d.set).collect::<HashSet<u32>>();

    let mut output = String::new();

    // iterate once per set that is defined somewhere
    for set in sets_list.iter() {
        let write_ty = descriptors.iter().filter(|d| d.set == *set)
                                  .map(|d| format!("::std::sync::Arc<{}>", d.bind_ty))
                                  .collect::<Vec<_>>();

        let writes = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                                .map(|(entry, d)| {
                                    let entry = if write_ty.len() == 1 {
                                        "".to_owned()
                                    } else {
                                        format!(".{}", entry)
                                    };

                                    format!("::vulkano::descriptor_set::DescriptorWrite {{
                                                 binding: {binding},
                                                 array_element: 0,
                                                 content: {bind_start} data{entry} {bind_end},
                                             }}", binding = d.binding, bind_start = d.bind_start,
                                                  bind_end = d.bind_end,
                                                  entry = entry)
                                })
                                .collect::<Vec<_>>();

        let descr = descriptors.iter().enumerate().filter(|&(_, d)| d.set == *set)
                               .map(|(entry, d)| {
                                   format!("::vulkano::descriptor_set::DescriptorDesc {{
                                                binding: {binding},
                                                ty: {desc_ty},
                                                array_count: 1,
                                                stages: ::vulkano::descriptor_set::ShaderStages::all_graphics(),        // TODO:
                                            }}", binding = d.binding, desc_ty = d.desc_ty)
                               })
                               .collect::<Vec<_>>();

        output.push_str(&format!(r#"
#[derive(Default)]
pub struct Set{set};

unsafe impl ::vulkano::descriptor_set::SetLayout for Set{set} {{
    fn descriptors(&self) -> Vec<::vulkano::descriptor_set::DescriptorDesc> {{
        vec![
            {descr}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutWrite<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        vec![
            {writes}
        ]
    }}
}}

unsafe impl ::vulkano::descriptor_set::SetLayoutInit<{write_ty}> for Set{set} {{
    fn decode(&self, data: {write_ty}) -> Vec<::vulkano::descriptor_set::DescriptorWrite> {{
        ::vulkano::descriptor_set::SetLayoutWrite::decode(self, data)
    }}
}}

"#, set = set, write_ty = write_ty.join(","), writes = writes.join(","), descr = descr.join(",")));
    }

    let max_set = sets_list.iter().cloned().max().map(|v| v + 1).unwrap_or(0);

    let sets_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSet<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let sets = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if sets_defs.len() == 1 {
                format!("sets")
            } else {
                format!("sets.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    let layouts_defs = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            format!("::std::sync::Arc<::vulkano::descriptor_set::DescriptorSetLayout<Set{}>>", num)
        } else {
            "()".to_owned()
        }
    }).collect::<Vec<_>>();

    let layouts = (0 .. max_set).map(|num| {
        if sets_list.contains(&num) {
            if layouts_defs.len() == 1 {
                format!("layouts")
            } else {
                format!("layouts.{}", num)
            }
        } else {
            "()".to_owned()     // FIXME: wrong
        }
    }).collect::<Vec<_>>();

    output.push_str(&format!(r#"
#[derive(Default)]
pub struct Layout;

unsafe impl ::vulkano::descriptor_set::Layout for Layout {{
    type DescriptorSets = ({sets_defs});
    type DescriptorSetLayouts = ({layouts_defs});
    type PushConstants = ();

    fn decode_descriptor_set_layouts(&self, layouts: Self::DescriptorSetLayouts)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSetLayout>>
    {{
        vec![
            {layouts}
        ]
    }}

    fn decode_descriptor_sets(&self, sets: Self::DescriptorSets)
        -> Vec<::std::sync::Arc<::vulkano::descriptor_set::AbstractDescriptorSet>>
    {{
        vec![
            {sets}
        ]
    }}
}}
"#, sets_defs = sets_defs.join(","), layouts_defs = layouts_defs.join(","),
    layouts = layouts.join(","), sets = sets.join(",")));

    output
}

/// Assumes that `variable` is a variable with a `TypePointer` and returns the id of the pointed
/// type.
fn pointer_variable_ty(doc: &parse::Spirv, variable: u32) -> u32 {
    let var_ty = doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::Variable { result_type_id, result_id, .. } if result_id == variable => {
                Some(result_type_id)
            },
            _ => None
        }
    }).next().unwrap();

    doc.instructions.iter().filter_map(|i| {
        match i {
            &parse::Instruction::TypePointer { result_id, type_id, .. } if result_id == var_ty => {
                Some(type_id)
            },
            _ => None
        }
    }).next().unwrap()
}
<|endoftext|>


<commit_before>use vk;

/// How the input assembly stage should behave.
#[derive(Copy, Clone, Debug)]
pub struct InputAssembly {
    /// The type of primitives.
    ///
    /// Note that some tologies don't support primitive restart.
    pub topology: PrimitiveTopology,

    /// If true, then the special index value `0xffff` or `0xffffffff` will tell the GPU that it is
    /// the end of the current primitive. A new primitive will restart at the next index.
    ///
    /// Note that some tologies don't support primitive restart.
    pub primitive_restart_enable: bool,
}

/// Describes how vertices must be grouped together to form primitives.
///
/// Note that some topologies don't support primitive restart.
#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum PrimitiveTopology {
    PointList = vk::PRIMITIVE_TOPOLOGY_POINT_LIST,
    LineList = vk::PRIMITIVE_TOPOLOGY_LINE_LIST,
    LineStrip = vk::PRIMITIVE_TOPOLOGY_LINE_STRIP,
    TriangleList = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
    TriangleStrip = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
    TriangleFan = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
    LineListWithAdjacency = vk::PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
    LineStripWithAdjacency = vk::PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
    TriangleListWithAdjancecy = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
    TriangleStripWithAdjacency = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
    PatchList = vk::PRIMITIVE_TOPOLOGY_PATCH_LIST,
}

impl PrimitiveTopology {
    /// Returns true if this primitive topology supports using primitives restart.
    #[inline]
    pub fn supports_primitive_restart(&self) -> bool {
        match *self {
            PrimitiveTopology::LineStrip => true,
            PrimitiveTopology::TriangleStrip => true,
            PrimitiveTopology::TriangleFan => true,
            PrimitiveTopology::LineStripWithAdjacency => true,
            PrimitiveTopology::TriangleStripWithAdjacency => true,
            _ => false
        }
    }
}

/// Trait for types that can be used as indices by the GPU.
pub unsafe trait Index {
    /// Returns the type of data.
    fn ty() -> IndexType;
}

unsafe impl Index for u16 {
    #[inline(always)]
    fn ty() -> IndexType {
        IndexType::U16
    }
}

unsafe impl Index for u32 {
    #[inline(always)]
    fn ty() -> IndexType {
        IndexType::U32
    }
}

/// An enumeration of all valid index types.
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
#[repr(u32)]
pub enum IndexType {
    U16 = vk::INDEX_TYPE_UINT16,
    U32 = vk::INDEX_TYPE_UINT32,
}
<commit_msg>Some docs to input_assembly<commit_after>//! Assembling vertices into primitives.
//!
//! The input assembly is the stage where lists of vertices are turned into primitives.
//!
use vk;

/// How the input assembly stage should behave.
#[derive(Copy, Clone, Debug)]
pub struct InputAssembly {
    /// The type of primitives.
    ///
    /// Note that some tologies don't support primitive restart.
    pub topology: PrimitiveTopology,

    /// If true, then the special index value `0xffff` or `0xffffffff` will tell the GPU that it is
    /// the end of the current primitive. A new primitive will restart at the next index.
    ///
    /// Note that some tologies don't support primitive restart.
    pub primitive_restart_enable: bool,
}

/// Describes how vertices must be grouped together to form primitives.
///
/// Note that some topologies don't support primitive restart.
#[derive(Copy, Clone, Debug)]
#[repr(u32)]
pub enum PrimitiveTopology {
    PointList = vk::PRIMITIVE_TOPOLOGY_POINT_LIST,
    LineList = vk::PRIMITIVE_TOPOLOGY_LINE_LIST,
    LineStrip = vk::PRIMITIVE_TOPOLOGY_LINE_STRIP,
    TriangleList = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
    TriangleStrip = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
    TriangleFan = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
    LineListWithAdjacency = vk::PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY,
    LineStripWithAdjacency = vk::PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY,
    TriangleListWithAdjancecy = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY,
    TriangleStripWithAdjacency = vk::PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY,
    PatchList = vk::PRIMITIVE_TOPOLOGY_PATCH_LIST,
}

impl PrimitiveTopology {
    /// Returns true if this primitive topology supports using primitives restart.
    #[inline]
    pub fn supports_primitive_restart(&self) -> bool {
        match *self {
            PrimitiveTopology::LineStrip => true,
            PrimitiveTopology::TriangleStrip => true,
            PrimitiveTopology::TriangleFan => true,
            PrimitiveTopology::LineStripWithAdjacency => true,
            PrimitiveTopology::TriangleStripWithAdjacency => true,
            _ => false
        }
    }
}

/// Trait for types that can be used as indices by the GPU.
pub unsafe trait Index {
    /// Returns the type of data.
    fn ty() -> IndexType;
}

unsafe impl Index for u16 {
    #[inline(always)]
    fn ty() -> IndexType {
        IndexType::U16
    }
}

unsafe impl Index for u32 {
    #[inline(always)]
    fn ty() -> IndexType {
        IndexType::U32
    }
}

/// An enumeration of all valid index types.
#[derive(Copy, Clone, Debug)]
#[allow(missing_docs)]
#[repr(u32)]
pub enum IndexType {
    U16 = vk::INDEX_TYPE_UINT16,
    U32 = vk::INDEX_TYPE_UINT32,
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! In the Vulkan API, command buffers must be allocated from *command pools*.
//!
//! A command pool holds and manages the memory of one or more command buffers. If you destroy a
//! command pool, all of its command buffers are automatically destroyed.
//!
//! In vulkano, creating a command buffer requires passing an implementation of the `CommandPool`
//! trait. By default vulkano will use the `StandardCommandPool` struct, but you can implement
//! this trait yourself by wrapping around the `UnsafeCommandPool` type.

pub use self::{
    standard::StandardCommandPool,
    sys::{CommandPoolTrimError, UnsafeCommandPool, UnsafeCommandPoolAlloc},
};
use super::CommandBufferLevel;
use crate::{
    device::{physical::QueueFamily, DeviceOwned},
    OomError,
};

pub mod standard;
mod sys;

/// Types that manage the memory of command buffers.
///
/// # Safety
///
/// A Vulkan command pool must be externally synchronized as if it owned the command buffers that
/// were allocated from it. This includes allocating from the pool, freeing from the pool,
/// resetting the pool or individual command buffers, and most importantly recording commands to
/// command buffers.
///
/// The implementation of `CommandPool` is expected to manage this. For as long as a `Builder`
/// is alive, the trait implementation is expected to lock the pool that allocated the `Builder`
/// for the current thread.
///
/// > **Note**: This may be modified in the future to allow different implementation strategies.
///
/// The destructors of the `CommandPoolBuilderAlloc` and the `CommandPoolAlloc` are expected to
/// free the command buffer, reset the command buffer, or add it to a pool so that it gets reused.
/// If the implementation frees or resets the command buffer, it must not forget that this
/// operation must lock the pool.
///
pub unsafe trait CommandPool: DeviceOwned {
    /// See `alloc()`.
    type Iter: Iterator<Item = Self::Builder>;
    /// Represents a command buffer that has been allocated and that is currently being built.
    type Builder: CommandPoolBuilderAlloc<Alloc = Self::Alloc>;
    /// Represents a command buffer that has been allocated and that is pending execution or is
    /// being executed.
    type Alloc: CommandPoolAlloc;

    /// Allocates command buffers from this pool.
    ///
    /// Returns an iterator that contains an bunch of allocated command buffers.
    fn allocate(
        &self,
        level: CommandBufferLevel,
        command_buffer_count: u32,
    ) -> Result<Self::Iter, OomError>;

    /// Returns the queue family that this pool targets.
    fn queue_family(&self) -> QueueFamily;
}

/// A command buffer allocated from a pool and that can be recorded.
///
/// # Safety
///
/// See `CommandPool` for information about safety.
///
pub unsafe trait CommandPoolBuilderAlloc: DeviceOwned {
    /// Return type of `into_alloc`.
    type Alloc: CommandPoolAlloc;

    /// Returns the internal object that contains the command buffer.
    fn inner(&self) -> &UnsafeCommandPoolAlloc;

    /// Turns this builder into a command buffer that is pending execution.
    fn into_alloc(self) -> Self::Alloc;

    /// Returns the queue family that the pool targets.
    fn queue_family(&self) -> QueueFamily;
}

/// A command buffer allocated from a pool that has finished being recorded.
///
/// # Safety
///
/// See `CommandPool` for information about safety.
///
pub unsafe trait CommandPoolAlloc: DeviceOwned + Send + Sync {
    /// Returns the internal object that contains the command buffer.
    fn inner(&self) -> &UnsafeCommandPoolAlloc;

    /// Returns the queue family that the pool targets.
    fn queue_family(&self) -> QueueFamily;
}
<commit_msg>Re-export `UnsafeCommandPoolCreateInfo` and `UnsafeCommandPoolCreationError` (#1866)<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! In the Vulkan API, command buffers must be allocated from *command pools*.
//!
//! A command pool holds and manages the memory of one or more command buffers. If you destroy a
//! command pool, all of its command buffers are automatically destroyed.
//!
//! In vulkano, creating a command buffer requires passing an implementation of the `CommandPool`
//! trait. By default vulkano will use the `StandardCommandPool` struct, but you can implement
//! this trait yourself by wrapping around the `UnsafeCommandPool` type.

pub use self::{
    standard::StandardCommandPool,
    sys::{
        CommandPoolTrimError, UnsafeCommandPool, UnsafeCommandPoolAlloc,
        UnsafeCommandPoolCreateInfo, UnsafeCommandPoolCreationError,
    },
};
use super::CommandBufferLevel;
use crate::{
    device::{physical::QueueFamily, DeviceOwned},
    OomError,
};

pub mod standard;
mod sys;

/// Types that manage the memory of command buffers.
///
/// # Safety
///
/// A Vulkan command pool must be externally synchronized as if it owned the command buffers that
/// were allocated from it. This includes allocating from the pool, freeing from the pool,
/// resetting the pool or individual command buffers, and most importantly recording commands to
/// command buffers.
///
/// The implementation of `CommandPool` is expected to manage this. For as long as a `Builder`
/// is alive, the trait implementation is expected to lock the pool that allocated the `Builder`
/// for the current thread.
///
/// > **Note**: This may be modified in the future to allow different implementation strategies.
///
/// The destructors of the `CommandPoolBuilderAlloc` and the `CommandPoolAlloc` are expected to
/// free the command buffer, reset the command buffer, or add it to a pool so that it gets reused.
/// If the implementation frees or resets the command buffer, it must not forget that this
/// operation must lock the pool.
///
pub unsafe trait CommandPool: DeviceOwned {
    /// See `alloc()`.
    type Iter: Iterator<Item = Self::Builder>;
    /// Represents a command buffer that has been allocated and that is currently being built.
    type Builder: CommandPoolBuilderAlloc<Alloc = Self::Alloc>;
    /// Represents a command buffer that has been allocated and that is pending execution or is
    /// being executed.
    type Alloc: CommandPoolAlloc;

    /// Allocates command buffers from this pool.
    ///
    /// Returns an iterator that contains an bunch of allocated command buffers.
    fn allocate(
        &self,
        level: CommandBufferLevel,
        command_buffer_count: u32,
    ) -> Result<Self::Iter, OomError>;

    /// Returns the queue family that this pool targets.
    fn queue_family(&self) -> QueueFamily;
}

/// A command buffer allocated from a pool and that can be recorded.
///
/// # Safety
///
/// See `CommandPool` for information about safety.
///
pub unsafe trait CommandPoolBuilderAlloc: DeviceOwned {
    /// Return type of `into_alloc`.
    type Alloc: CommandPoolAlloc;

    /// Returns the internal object that contains the command buffer.
    fn inner(&self) -> &UnsafeCommandPoolAlloc;

    /// Turns this builder into a command buffer that is pending execution.
    fn into_alloc(self) -> Self::Alloc;

    /// Returns the queue family that the pool targets.
    fn queue_family(&self) -> QueueFamily;
}

/// A command buffer allocated from a pool that has finished being recorded.
///
/// # Safety
///
/// See `CommandPool` for information about safety.
///
pub unsafe trait CommandPoolAlloc: DeviceOwned + Send + Sync {
    /// Returns the internal object that contains the command buffer.
    fn inner(&self) -> &UnsafeCommandPoolAlloc;

    /// Returns the queue family that the pool targets.
    fn queue_family(&self) -> QueueFamily;
}
<|endoftext|>


<commit_before>/**
use the taylor series to caculate tan^-1 (x) where |x| <= 1, x >= 0
**/
fn taylor_atan (x: f64) -> f64 {
    let mut a: f64 = 0.0;
    let mut s: f64 = 1.0;
    let mut xpow: f64;

    // by definition
    if x == 0.0 {
        return 0.0;
    }

    // this is well known, and the taylor series we are using explictly assumes |x| < 1
    if x == 1.0 {
        return std::f64::consts::PI/4.0;
    }

    for ii in range(1i, 60) {
        if ii % 2 == 0 {
            continue;
        }
        xpow = 1.0;
        for jj in range(0i, ii) {
            xpow *= x;
        }
        a += s * xpow / (ii as f64);
        s = -1.0*s;
    }

    return a;
}

fn generate_table () {

    let pi: f64 = 3.1415926536897932384626;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    println!("let cordic_tab = [");

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = taylor_atan(ipow) * (mul as f64);

        if ii % 4 == 0 && ii > 0 {
            println!("");
        }
        if ii == 0 {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        } else {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        }
    }

    println!("];");
}

fn sin(theta: f64) -> f64 {

    let num_bits: uint = 32;

    let k1: f64 = 0.6072529350088812561694; // 1/k
    let muli: int = 1<<(num_bits-2);
    let mul: f64 = muli as f64;
    let cordic_k1: f64 = k1 * mul;
    let mut x: int = cordic_k1.floor() as int; //0x26DD3B6A;
    let mut y: int = 0;
    let mut z: int = (mul * theta).floor() as int;
    let mut tx: int;
    let mut ty: int;
    let mut tz: int;

    let mut d: int;

    let cordic_tab = [
        0x3243f6a8, 0x1dac6705, 0xfadbafc, 0x7f56ea6,
        0x3feab76, 0x1ffd55b, 0xfffaaa, 0x7fff55,
        0x3fffea, 0x1ffffd, 0xfffff, 0x7ffff,
        0x3ffff, 0x1ffff, 0xffff, 0x7fff,
        0x3fff, 0x1fff, 0xfff, 0x7ff,
        0x3ff, 0x1ff, 0xff, 0x7f,
        0x3f, 0x1f, 0xf, 0x8,
        0x4, 0x2, 0x1, 0x0, ];

    for k in range(0, num_bits) {
        if z >= 0 {
            d = 0;
        } else {
            d = -1;
        }

        tx = x - (((y>>k) ^ d) - d);
        ty = y + (((x>>k) ^ d) - d);
        tz = z - ((cordic_tab[k] ^ d) - d);
        x = tx; y = ty; z = tz;
    }

    let s: f64 = (y as f64)/(mul as f64);

    return s;

}

fn main() {
    generate_table();

    let frac_pi_2: f64 = std::f64::consts::FRAC_PI_2; //pi/2.0;

    for ii in range(0i, 50) {
        let cur: f64 = ii as f64;
        let theta: f64 = cur/50.0 * frac_pi_2;
        let s: f64 = sin(theta);
            println!("s {}", s);
    }
}
<commit_msg>simplify bit-shift conversion to f64<commit_after>/**
use the taylor series to caculate tan^-1 (x) where |x| <= 1, x >= 0
**/
fn taylor_atan (x: f64) -> f64 {
    let mut a: f64 = 0.0;
    let mut s: f64 = 1.0;
    let mut xpow: f64;

    // by definition
    if x == 0.0 {
        return 0.0;
    }

    // this is well known, and the taylor series we are using explictly assumes |x| < 1
    if x == 1.0 {
        return std::f64::consts::PI/4.0;
    }

    for ii in range(1i, 60) {
        if ii % 2 == 0 {
            continue;
        }
        xpow = 1.0;
        for jj in range(0i, ii) {
            xpow *= x;
        }
        a += s * xpow / (ii as f64);
        s = -1.0*s;
    }

    return a;
}

fn generate_table () {

    let pi: f64 = 3.1415926536897932384626;
    let k1: f64 = 0.6072529350088812561694; // 1/k
    let num_bits: uint = 32;
    let mul: uint = 1<<(num_bits-2);

    println!("Cordic sin in rust");
    println!("num bits {}", num_bits);
    println!("mul {}", mul);
    println!("1<<(num_bits-2) {}", ((1i << (num_bits-2)) as f64));
    println!("pi is {}", pi);
    println!("k1 is {}", k1);

    println!("let cordic_tab = [");

    let shift: f64 = 2.0;
    for ii in range(0, num_bits) {
        let ipow: f64 = 1f64/shift.powi(ii as i32);
        let cur: f64 = taylor_atan(ipow) * (mul as f64);

        if ii % 4 == 0 && ii > 0 {
            println!("");
        }
        if ii == 0 {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        } else {
            print!("0x{}, ", std::f64::to_str_hex(cur.floor()));
        }
    }

    println!("];");
}

fn sin(theta: f64) -> f64 {

    let num_bits: uint = 32;

    let k1: f64 = 0.6072529350088812561694; // 1/k
    let mul: f64 = (1i << (num_bits-2)) as f64;
    let cordic_k1: f64 = k1 * mul;
    let mut x: int = cordic_k1.floor() as int; //0x26DD3B6A;
    let mut y: int = 0;
    let mut z: int = (mul * theta).floor() as int;
    let mut tx: int;
    let mut ty: int;
    let mut tz: int;

    let mut d: int;

    let cordic_tab = [
        0x3243f6a8, 0x1dac6705, 0xfadbafc, 0x7f56ea6,
        0x3feab76, 0x1ffd55b, 0xfffaaa, 0x7fff55,
        0x3fffea, 0x1ffffd, 0xfffff, 0x7ffff,
        0x3ffff, 0x1ffff, 0xffff, 0x7fff,
        0x3fff, 0x1fff, 0xfff, 0x7ff,
        0x3ff, 0x1ff, 0xff, 0x7f,
        0x3f, 0x1f, 0xf, 0x8,
        0x4, 0x2, 0x1, 0x0, ];

    for k in range(0, num_bits) {
        if z >= 0 {
            d = 0;
        } else {
            d = -1;
        }

        tx = x - (((y>>k) ^ d) - d);
        ty = y + (((x>>k) ^ d) - d);
        tz = z - ((cordic_tab[k] ^ d) - d);
        x = tx; y = ty; z = tz;
    }

    let s: f64 = (y as f64)/(mul as f64);

    return s;

}

fn main() {
    generate_table();

    let frac_pi_2: f64 = std::f64::consts::FRAC_PI_2; //pi/2.0;

    for ii in range(0i, 50) {
        let cur: f64 = ii as f64;
        let theta: f64 = cur/50.0 * frac_pi_2;
        let s: f64 = sin(theta);
            println!("s {}", s);
    }
}
<|endoftext|>


<commit_before>//! Missing batteries for standard libraries.
use std::{cmp::Ordering, ops, time::Instant};

mod macros;
pub mod process;
pub mod panic_context;

pub use always_assert::{always, never};

#[inline(always)]
pub fn is_ci() -> bool {
    option_env!("CI").is_some()
}

#[must_use]
pub fn timeit(label: &'static str) -> impl Drop {
    let start = Instant::now();
    defer(move || eprintln!("{}: {:.2?}", label, start.elapsed()))
}

/// Prints backtrace to stderr, useful for debugging.
#[cfg(feature = "backtrace")]
pub fn print_backtrace() {
    let bt = backtrace::Backtrace::new();
    eprintln!("{:?}", bt);
}
#[cfg(not(feature = "backtrace"))]
pub fn print_backtrace() {
    eprintln!(
        r#"Enable the backtrace feature.
Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
"#
    );
}

pub fn to_lower_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_lowercase)
}
pub fn to_upper_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_uppercase)
}
fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
    let mut buf = String::with_capacity(s.len());
    let mut prev = false;
    for c in s.chars() {
        // `&& prev` is required to not insert `_` before the first symbol.
        if c.is_ascii_uppercase() && prev {
            // This check is required to not translate `Weird_Case` into `weird__case`.
            if !buf.ends_with('_') {
                buf.push('_')
            }
        }
        prev = true;

        buf.push(change_case(&c));
    }
    buf
}

pub fn replace(buf: &mut String, from: char, to: &str) {
    if !buf.contains(from) {
        return;
    }
    // FIXME: do this in place.
    *buf = buf.replace(from, to)
}

pub fn trim_indent(mut text: &str) -> String {
    if text.starts_with('\n') {
        text = &text[1..];
    }
    let indent = text
        .lines()
        .filter(|it| !it.trim().is_empty())
        .map(|it| it.len() - it.trim_start().len())
        .min()
        .unwrap_or(0);
    text.split_inclusive('\n')
        .map(
            |line| {
                if line.len() <= indent {
                    line.trim_start_matches(' ')
                } else {
                    &line[indent..]
                }
            },
        )
        .collect()
}

pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
where
    F: FnMut(&T) -> Ordering,
{
    let start = slice.partition_point(|it| key(it) == Ordering::Less);
    let len = slice[start..].partition_point(|it| key(it) == Ordering::Equal);
    start..start + len
}

#[must_use]
pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
    struct D<F: FnOnce()>(Option<F>);
    impl<F: FnOnce()> Drop for D<F> {
        fn drop(&mut self) {
            if let Some(f) = self.0.take() {
                f()
            }
        }
    }
    D(Some(f))
}

#[cfg_attr(not(target_arch = "wasm32"), repr(transparent))]
#[derive(Debug)]
pub struct JodChild(pub std::process::Child);

impl ops::Deref for JodChild {
    type Target = std::process::Child;
    fn deref(&self) -> &std::process::Child {
        &self.0
    }
}

impl ops::DerefMut for JodChild {
    fn deref_mut(&mut self) -> &mut std::process::Child {
        &mut self.0
    }
}

impl Drop for JodChild {
    fn drop(&mut self) {
        let _ = self.0.kill();
        let _ = self.0.wait();
    }
}

impl JodChild {
    pub fn into_inner(self) -> std::process::Child {
        if cfg!(target_arch = "wasm32") {
            panic!("no processes on wasm");
        }
        // SAFETY: repr transparent, except on WASM
        unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
    }
}

// feature: iter_order_by
// Iterator::eq_by
pub fn iter_eq_by<I, I2, F>(this: I2, other: I, mut eq: F) -> bool
where
    I: IntoIterator,
    I2: IntoIterator,
    F: FnMut(I2::Item, I::Item) -> bool,
{
    let mut other = other.into_iter();
    let mut this = this.into_iter();

    loop {
        let x = match this.next() {
            None => return other.next().is_none(),
            Some(val) => val,
        };

        let y = match other.next() {
            None => return false,
            Some(val) => val,
        };

        if !eq(x, y) {
            return false;
        }
    }
}

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

    #[test]
    fn test_trim_indent() {
        assert_eq!(trim_indent(""), "");
        assert_eq!(
            trim_indent(
                "
            hello
            world
"
            ),
            "hello\nworld\n"
        );
        assert_eq!(
            trim_indent(
                "
            hello
            world"
            ),
            "hello\nworld"
        );
        assert_eq!(trim_indent("    hello\n    world\n"), "hello\nworld\n");
        assert_eq!(
            trim_indent(
                "
            fn main() {
                return 92;
            }
        "
            ),
            "fn main() {\n    return 92;\n}\n"
        );
    }
}
<commit_msg>minor: improve readability<commit_after>//! Missing batteries for standard libraries.
use std::{cmp::Ordering, ops, time::Instant};

mod macros;
pub mod process;
pub mod panic_context;

pub use always_assert::{always, never};

#[inline(always)]
pub fn is_ci() -> bool {
    option_env!("CI").is_some()
}

#[must_use]
pub fn timeit(label: &'static str) -> impl Drop {
    let start = Instant::now();
    defer(move || eprintln!("{}: {:.2?}", label, start.elapsed()))
}

/// Prints backtrace to stderr, useful for debugging.
pub fn print_backtrace() {
    #[cfg(feature = "backtrace")]
    eprintln!("{:?}", backtrace::Backtrace::new());

    #[cfg(not(feature = "backtrace"))]
    eprintln!(
        r#"Enable the backtrace feature.
Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
"#
    );
}

pub fn to_lower_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_lowercase)
}
pub fn to_upper_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_uppercase)
}
fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
    let mut buf = String::with_capacity(s.len());
    let mut prev = false;
    for c in s.chars() {
        // `&& prev` is required to not insert `_` before the first symbol.
        if c.is_ascii_uppercase() && prev {
            // This check is required to not translate `Weird_Case` into `weird__case`.
            if !buf.ends_with('_') {
                buf.push('_')
            }
        }
        prev = true;

        buf.push(change_case(&c));
    }
    buf
}

pub fn replace(buf: &mut String, from: char, to: &str) {
    if !buf.contains(from) {
        return;
    }
    // FIXME: do this in place.
    *buf = buf.replace(from, to)
}

pub fn trim_indent(mut text: &str) -> String {
    if text.starts_with('\n') {
        text = &text[1..];
    }
    let indent = text
        .lines()
        .filter(|it| !it.trim().is_empty())
        .map(|it| it.len() - it.trim_start().len())
        .min()
        .unwrap_or(0);
    text.split_inclusive('\n')
        .map(
            |line| {
                if line.len() <= indent {
                    line.trim_start_matches(' ')
                } else {
                    &line[indent..]
                }
            },
        )
        .collect()
}

pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
where
    F: FnMut(&T) -> Ordering,
{
    let start = slice.partition_point(|it| key(it) == Ordering::Less);
    let len = slice[start..].partition_point(|it| key(it) == Ordering::Equal);
    start..start + len
}

#[must_use]
pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
    struct D<F: FnOnce()>(Option<F>);
    impl<F: FnOnce()> Drop for D<F> {
        fn drop(&mut self) {
            if let Some(f) = self.0.take() {
                f()
            }
        }
    }
    D(Some(f))
}

#[cfg_attr(not(target_arch = "wasm32"), repr(transparent))]
#[derive(Debug)]
pub struct JodChild(pub std::process::Child);

impl ops::Deref for JodChild {
    type Target = std::process::Child;
    fn deref(&self) -> &std::process::Child {
        &self.0
    }
}

impl ops::DerefMut for JodChild {
    fn deref_mut(&mut self) -> &mut std::process::Child {
        &mut self.0
    }
}

impl Drop for JodChild {
    fn drop(&mut self) {
        let _ = self.0.kill();
        let _ = self.0.wait();
    }
}

impl JodChild {
    pub fn into_inner(self) -> std::process::Child {
        if cfg!(target_arch = "wasm32") {
            panic!("no processes on wasm");
        }
        // SAFETY: repr transparent, except on WASM
        unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
    }
}

// feature: iter_order_by
// Iterator::eq_by
pub fn iter_eq_by<I, I2, F>(this: I2, other: I, mut eq: F) -> bool
where
    I: IntoIterator,
    I2: IntoIterator,
    F: FnMut(I2::Item, I::Item) -> bool,
{
    let mut other = other.into_iter();
    let mut this = this.into_iter();

    loop {
        let x = match this.next() {
            None => return other.next().is_none(),
            Some(val) => val,
        };

        let y = match other.next() {
            None => return false,
            Some(val) => val,
        };

        if !eq(x, y) {
            return false;
        }
    }
}

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

    #[test]
    fn test_trim_indent() {
        assert_eq!(trim_indent(""), "");
        assert_eq!(
            trim_indent(
                "
            hello
            world
"
            ),
            "hello\nworld\n"
        );
        assert_eq!(
            trim_indent(
                "
            hello
            world"
            ),
            "hello\nworld"
        );
        assert_eq!(trim_indent("    hello\n    world\n"), "hello\nworld\n");
        assert_eq!(
            trim_indent(
                "
            fn main() {
                return 92;
            }
        "
            ),
            "fn main() {\n    return 92;\n}\n"
        );
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Exonum Team
//
// 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.

// This is a regression test for exonum configuration.
extern crate exonum;
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use exonum::{
    api::backends::actix::AllowOrigin, crypto::PublicKey, encoding::serialize::FromHex,
    helpers::{
        config::{ConfigFile, ConfigManager}, fabric::NodeBuilder,
    },
    node::{ConnectInfo, ConnectListConfig, NodeConfig},
};
use toml::Value;

use std::{
    ffi::OsString, fs, fs::{File, OpenOptions}, io::{Read, Write},
    net::{SocketAddr, ToSocketAddrs}, panic, path::Path, str::FromStr,
};

const CONFIG_TMP_FOLDER: &str = "/tmp/";
const CONFIG_TESTDATA_FOLDER: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/config/");

const GENERATED_TEMPLATE: &str = "template.toml";

const SEC_CONFIG: [&str; 4] = [
    "config0_sec.toml",
    "config1_sec.toml",
    "config2_sec.toml",
    "config3_sec.toml",
];

const PUB_CONFIG: [&str; 4] = [
    "config0_pub.toml",
    "config1_pub.toml",
    "config2_pub.toml",
    "config3_pub.toml",
];

fn full_tmp_folder(folder: &str) -> String {
    format!("{}exonum-test-{}/", CONFIG_TMP_FOLDER, folder)
}

fn full_tmp_name(filename: &str, folder: &str) -> String {
    format!("{}{}", full_tmp_folder(folder), filename)
}

fn full_testdata_name(filename: &str) -> String {
    format!("{}{}", CONFIG_TESTDATA_FOLDER, filename)
}

fn touch(path: &str) {
    OpenOptions::new()
        .create(true)
        .write(true)
        .open(path)
        .unwrap();
}

fn compare_files(filename: &str, folder: &str) {
    let source = full_testdata_name(filename);
    let destination = full_tmp_name(filename, folder);

    let mut source = File::open(source).unwrap();
    let mut destination = File::open(destination).unwrap();

    let mut source_buffer = String::new();
    let mut destination_buffer = String::new();

    let len = source.read_to_string(&mut source_buffer).unwrap();
    destination.read_to_string(&mut destination_buffer).unwrap();

    assert!(len > 0);
    let source_toml: toml::Value = toml::de::from_str(&source_buffer).unwrap();
    let destination_toml: toml::Value = toml::de::from_str(&destination_buffer).unwrap();
    assert_eq!(source_toml, destination_toml);
}

fn default_run_with_matches<I, T>(iter: I) -> bool
where
    I: IntoIterator<Item = T>,
    T: Into<OsString> + Clone,
{
    let builder = NodeBuilder::new();
    builder.parse_cmd_string(iter)
}

fn generate_template(folder: &str) {
    assert!(!default_run_with_matches(vec![
        "exonum-config-test",
        "generate-template",
        &full_tmp_name(GENERATED_TEMPLATE, folder),
        "--validators-count",
        "1",
    ]));
}

#[derive(Debug, Clone, Copy)]
enum IpMode {
    V4,
    V6,
}

fn generate_config(folder: &str, i: usize, mode: IpMode) {
    let ip = match mode {
        IpMode::V4 => "127.0.0.1",
        IpMode::V6 => "::1",
    };
    assert!(!default_run_with_matches(vec![
        "exonum-config-test",
        "generate-config",
        &full_testdata_name(GENERATED_TEMPLATE),
        &full_tmp_name(PUB_CONFIG[i], folder),
        &full_tmp_name(SEC_CONFIG[i], folder),
        "-a",
        ip,
    ]));
}

fn finalize_config(folder: &str, config: &str, i: usize, count: usize) {
    let mut variables = vec![
        "exonum-config-test".to_owned(),
        "finalize".to_owned(),
        full_testdata_name(SEC_CONFIG[i]),
        full_tmp_name(config, folder),
        "-p".to_owned(),
    ];

    fs::create_dir_all(full_tmp_name("", folder)).expect("Can't create temp folder");

    for &conf in PUB_CONFIG.iter().take(count) {
        override_validators_count(conf, count, folder);
        variables.push(full_tmp_name(conf, folder));
    }
    assert!(!default_run_with_matches(variables));
}

fn override_validators_count(config: &str, n: usize, folder: &str) {
    let res = {
        let mut contents = String::new();
        let mut file = File::open(full_testdata_name(config)).unwrap();
        file.read_to_string(&mut contents)
            .expect("Read from config file failed");

        let mut value = contents.as_str().parse::<Value>().unwrap();
        {
            let mut count = value
                .get_mut("common")
                .unwrap()
                .get_mut("general_config")
                .unwrap()
                .as_table_mut()
                .unwrap();

            count.insert("validators_count".into(), Value::from(n as u8));
        }

        toml::to_string(&value).unwrap()
    };

    File::create(full_tmp_name(config, folder))
        .unwrap()
        .write_all(res.as_bytes())
        .expect("Create temp config file is failed");
}

fn run_node(config: &str, folder: &str) {
    assert!(default_run_with_matches(vec![
        "exonum-config-test",
        "run",
        "-c",
        &full_testdata_name(config),
        "-d",
        &full_tmp_folder(folder),
    ]));
}

fn run_dev(folder: &str) {
    assert!(default_run_with_matches(vec![
        "exonum-config-test",
        "run-dev",
        "-a",
        &full_tmp_folder(folder),
    ]));
}

#[test]
fn test_generate_template() {
    let command = "generate-template";

    let result = panic::catch_unwind(|| {
        generate_template(command);
        compare_files(GENERATED_TEMPLATE, command);
    });

    fs::remove_dir_all(full_tmp_folder(command)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

fn test_generate_config(mode: IpMode) {
    // Important because tests run in parallel, folder names should be different.
    let command = match mode {
        IpMode::V4 => "generate-config-ipv4",
        IpMode::V6 => "generate-config-ipv6",
    };

    let result = panic::catch_unwind(|| {
        for i in 0..PUB_CONFIG.len() {
            generate_config(command, i, mode);
        }
    });

    fs::remove_dir_all(full_tmp_folder(command)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

#[test]
fn test_generate_config_ipv4() {
    test_generate_config(IpMode::V4);
}

#[test]
fn test_generate_config_ipv6() {
    test_generate_config(IpMode::V6);
}

#[test]
fn test_generate_full_config_run() {
    let command = "finalize";
    let result = panic::catch_unwind(|| {
        for i in 0..PUB_CONFIG.len() {
            for n in 0..PUB_CONFIG.len() + 1 {
                println!("{} {}", i, n);
                let config = format!("config{}{}.toml", i, n);
                let result = panic::catch_unwind(|| {
                    finalize_config(command, &config, i, n);
                    compare_files(&config, command);
                    run_node(&config, command);
                });

                // if we trying to create config,
                // without our config, this is a problem
                if n <= i || n == 0 {
                    assert!(result.is_err());
                } else {
                    assert!(result.is_ok());
                }
            }
        }
    });

    fs::remove_dir_all(full_tmp_folder(command)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

#[test]
fn test_run_dev() {
    let artifacts_dir = ".exonum";
    let db_dir = format!("{}/{}", artifacts_dir, "db");
    let full_db_dir = full_tmp_folder(&db_dir);

    // Mock existence of old DB files that are supposed to be cleaned up.
    fs::create_dir_all(Path::new(&full_db_dir)).expect("Expected db temp folder to be created.");
    let old_db_file = full_tmp_name("1", &db_dir);
    touch(&old_db_file);

    let result = panic::catch_unwind(|| {
        run_dev(artifacts_dir);

        // Test cleaning up.
        assert!(!Path::new(&old_db_file).exists());
    });

    fs::remove_dir_all(full_tmp_folder(artifacts_dir)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

#[test]
fn allow_origin_toml() {
    fn check(text: &str, allow_origin: AllowOrigin) {
        #[derive(Serialize, Deserialize)]
        struct Config {
            allow_origin: AllowOrigin,
        }
        let config_toml = format!("allow_origin = {}\n", text);
        let config: Config = ::toml::from_str(&config_toml).unwrap();
        assert_eq!(config.allow_origin, allow_origin);
        assert_eq!(::toml::to_string(&config).unwrap(), config_toml);
    }

    check(r#""*""#, AllowOrigin::Any);
    check(
        r#""http://example.com""#,
        AllowOrigin::Whitelist(vec!["http://example.com".to_string()]),
    );
    check(
        r#"["http://a.org", "http://b.org"]"#,
        AllowOrigin::Whitelist(vec!["http://a.org".to_string(), "http://b.org".to_string()]),
    );
}

#[test]
fn test_update_config() {
    const TEST_DIR: &str = "config-update";
    const TEST_CONFIG_FILE: &str = "config01.toml";

    let full_test_dir = full_tmp_folder(TEST_DIR);
    fs::create_dir_all(Path::new(&full_test_dir))
        .expect("Expected test temp folder to be created.");

    // Copy test config to the separate file.
    let config_path = full_tmp_name(TEST_CONFIG_FILE, TEST_DIR);
    let testdata_path = full_testdata_name(TEST_CONFIG_FILE);
    fs::copy(testdata_path, config_path.clone()).unwrap();

    // Test config update.
    let peer = ConnectInfo {
        address: SocketAddr::from_str("0.0.0.1:8080").unwrap(),
        public_key: PublicKey::new([1; 32]),
    };

    let connect_list = ConnectListConfig { peers: vec![peer] };

    ConfigManager::update_connect_list(connect_list.clone(), &config_path)
        .expect("Unable to update connect list");
    let config: NodeConfig =
        ConfigFile::load(config_path.clone()).expect("Can't load node config file");

    let new_connect_list = config.connect_list;
    assert_eq!(new_connect_list.peers, connect_list.peers);

    // Cleanup.
    fs::remove_dir_all(Path::new(&full_test_dir)).unwrap();
}

#[test]
fn test_domain_name_peer() {
    const TEST_CONFIG_FILE: &str = "config_domain.toml";
    let testdata_path = full_testdata_name(TEST_CONFIG_FILE);
    let config: ConnectListConfig =
        ConfigFile::load(testdata_path).expect("Can't load node config file");

    let resolve_address = |a: &str| {
        a.to_socket_addrs()
            .expect(&format!("unable to resolve name from hostname: {}", a))
            .next()
            .expect(&format!("no one ip belongs to the hostname: {}", a))
    };

    let connect_info = match resolve_address("localhost:6333") {
        SocketAddr::V4(..) => ConnectInfo {
            address: "127.0.0.1:6333".parse().unwrap(),
            public_key: PublicKey::from_hex(
                "648e98a2405a40325d946bf8de6937795fe5c22ab095bca765a8b218e49ff5a3",
            ).unwrap(),
        },
        SocketAddr::V6(..) => ConnectInfo {
            address: "[::1]:6333".parse().unwrap(),
            public_key: PublicKey::from_hex(
                "648e98a2405a40325d946bf8de6937795fe5c22ab095bca765a8b218e49ff5a3",
            ).unwrap(),
        },
    };

    let connect_list = ConnectListConfig {
        peers: vec![
            ConnectInfo {
                address: "127.0.0.1:6333".parse().unwrap(),
                public_key: PublicKey::from_hex(
                    "16ef83ca4b231404daec6d07b24beb84d89c25944285d2e32a2dcf8f0f3eda72",
                ).unwrap(),
            },
            connect_info,
            ConnectInfo {
                address: "94.130.16.228:6333".parse().unwrap(),
                public_key: PublicKey::from_hex(
                    "924625eb77b9ad21e76713e7ada715945fbf0a926698832e121484c797fcc58e",
                ).unwrap(),
            },
        ],
    };

    assert_eq!(config.peers, connect_list.peers);
}
<commit_msg>Use PUBLIC_KEY_LENGTH constant instead of hardcoded integer (#954)<commit_after>// Copyright 2018 The Exonum Team
//
// 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.

// This is a regression test for exonum configuration.
extern crate exonum;
#[macro_use]
extern crate pretty_assertions;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use exonum::{
    api::backends::actix::AllowOrigin, crypto::{PublicKey, PUBLIC_KEY_LENGTH},
    encoding::serialize::FromHex,
    helpers::{
        config::{ConfigFile, ConfigManager}, fabric::NodeBuilder,
    },
    node::{ConnectInfo, ConnectListConfig, NodeConfig},
};
use toml::Value;

use std::{
    ffi::OsString, fs, fs::{File, OpenOptions}, io::{Read, Write},
    net::{SocketAddr, ToSocketAddrs}, panic, path::Path, str::FromStr,
};

const CONFIG_TMP_FOLDER: &str = "/tmp/";
const CONFIG_TESTDATA_FOLDER: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/config/");

const GENERATED_TEMPLATE: &str = "template.toml";

const SEC_CONFIG: [&str; 4] = [
    "config0_sec.toml",
    "config1_sec.toml",
    "config2_sec.toml",
    "config3_sec.toml",
];

const PUB_CONFIG: [&str; 4] = [
    "config0_pub.toml",
    "config1_pub.toml",
    "config2_pub.toml",
    "config3_pub.toml",
];

fn full_tmp_folder(folder: &str) -> String {
    format!("{}exonum-test-{}/", CONFIG_TMP_FOLDER, folder)
}

fn full_tmp_name(filename: &str, folder: &str) -> String {
    format!("{}{}", full_tmp_folder(folder), filename)
}

fn full_testdata_name(filename: &str) -> String {
    format!("{}{}", CONFIG_TESTDATA_FOLDER, filename)
}

fn touch(path: &str) {
    OpenOptions::new()
        .create(true)
        .write(true)
        .open(path)
        .unwrap();
}

fn compare_files(filename: &str, folder: &str) {
    let source = full_testdata_name(filename);
    let destination = full_tmp_name(filename, folder);

    let mut source = File::open(source).unwrap();
    let mut destination = File::open(destination).unwrap();

    let mut source_buffer = String::new();
    let mut destination_buffer = String::new();

    let len = source.read_to_string(&mut source_buffer).unwrap();
    destination.read_to_string(&mut destination_buffer).unwrap();

    assert!(len > 0);
    let source_toml: toml::Value = toml::de::from_str(&source_buffer).unwrap();
    let destination_toml: toml::Value = toml::de::from_str(&destination_buffer).unwrap();
    assert_eq!(source_toml, destination_toml);
}

fn default_run_with_matches<I, T>(iter: I) -> bool
where
    I: IntoIterator<Item = T>,
    T: Into<OsString> + Clone,
{
    let builder = NodeBuilder::new();
    builder.parse_cmd_string(iter)
}

fn generate_template(folder: &str) {
    assert!(!default_run_with_matches(vec![
        "exonum-config-test",
        "generate-template",
        &full_tmp_name(GENERATED_TEMPLATE, folder),
        "--validators-count",
        "1",
    ]));
}

#[derive(Debug, Clone, Copy)]
enum IpMode {
    V4,
    V6,
}

fn generate_config(folder: &str, i: usize, mode: IpMode) {
    let ip = match mode {
        IpMode::V4 => "127.0.0.1",
        IpMode::V6 => "::1",
    };
    assert!(!default_run_with_matches(vec![
        "exonum-config-test",
        "generate-config",
        &full_testdata_name(GENERATED_TEMPLATE),
        &full_tmp_name(PUB_CONFIG[i], folder),
        &full_tmp_name(SEC_CONFIG[i], folder),
        "-a",
        ip,
    ]));
}

fn finalize_config(folder: &str, config: &str, i: usize, count: usize) {
    let mut variables = vec![
        "exonum-config-test".to_owned(),
        "finalize".to_owned(),
        full_testdata_name(SEC_CONFIG[i]),
        full_tmp_name(config, folder),
        "-p".to_owned(),
    ];

    fs::create_dir_all(full_tmp_name("", folder)).expect("Can't create temp folder");

    for &conf in PUB_CONFIG.iter().take(count) {
        override_validators_count(conf, count, folder);
        variables.push(full_tmp_name(conf, folder));
    }
    assert!(!default_run_with_matches(variables));
}

fn override_validators_count(config: &str, n: usize, folder: &str) {
    let res = {
        let mut contents = String::new();
        let mut file = File::open(full_testdata_name(config)).unwrap();
        file.read_to_string(&mut contents)
            .expect("Read from config file failed");

        let mut value = contents.as_str().parse::<Value>().unwrap();
        {
            let mut count = value
                .get_mut("common")
                .unwrap()
                .get_mut("general_config")
                .unwrap()
                .as_table_mut()
                .unwrap();

            count.insert("validators_count".into(), Value::from(n as u8));
        }

        toml::to_string(&value).unwrap()
    };

    File::create(full_tmp_name(config, folder))
        .unwrap()
        .write_all(res.as_bytes())
        .expect("Create temp config file is failed");
}

fn run_node(config: &str, folder: &str) {
    assert!(default_run_with_matches(vec![
        "exonum-config-test",
        "run",
        "-c",
        &full_testdata_name(config),
        "-d",
        &full_tmp_folder(folder),
    ]));
}

fn run_dev(folder: &str) {
    assert!(default_run_with_matches(vec![
        "exonum-config-test",
        "run-dev",
        "-a",
        &full_tmp_folder(folder),
    ]));
}

#[test]
fn test_generate_template() {
    let command = "generate-template";

    let result = panic::catch_unwind(|| {
        generate_template(command);
        compare_files(GENERATED_TEMPLATE, command);
    });

    fs::remove_dir_all(full_tmp_folder(command)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

fn test_generate_config(mode: IpMode) {
    // Important because tests run in parallel, folder names should be different.
    let command = match mode {
        IpMode::V4 => "generate-config-ipv4",
        IpMode::V6 => "generate-config-ipv6",
    };

    let result = panic::catch_unwind(|| {
        for i in 0..PUB_CONFIG.len() {
            generate_config(command, i, mode);
        }
    });

    fs::remove_dir_all(full_tmp_folder(command)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

#[test]
fn test_generate_config_ipv4() {
    test_generate_config(IpMode::V4);
}

#[test]
fn test_generate_config_ipv6() {
    test_generate_config(IpMode::V6);
}

#[test]
fn test_generate_full_config_run() {
    let command = "finalize";
    let result = panic::catch_unwind(|| {
        for i in 0..PUB_CONFIG.len() {
            for n in 0..PUB_CONFIG.len() + 1 {
                println!("{} {}", i, n);
                let config = format!("config{}{}.toml", i, n);
                let result = panic::catch_unwind(|| {
                    finalize_config(command, &config, i, n);
                    compare_files(&config, command);
                    run_node(&config, command);
                });

                // if we trying to create config,
                // without our config, this is a problem
                if n <= i || n == 0 {
                    assert!(result.is_err());
                } else {
                    assert!(result.is_ok());
                }
            }
        }
    });

    fs::remove_dir_all(full_tmp_folder(command)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

#[test]
fn test_run_dev() {
    let artifacts_dir = ".exonum";
    let db_dir = format!("{}/{}", artifacts_dir, "db");
    let full_db_dir = full_tmp_folder(&db_dir);

    // Mock existence of old DB files that are supposed to be cleaned up.
    fs::create_dir_all(Path::new(&full_db_dir)).expect("Expected db temp folder to be created.");
    let old_db_file = full_tmp_name("1", &db_dir);
    touch(&old_db_file);

    let result = panic::catch_unwind(|| {
        run_dev(artifacts_dir);

        // Test cleaning up.
        assert!(!Path::new(&old_db_file).exists());
    });

    fs::remove_dir_all(full_tmp_folder(artifacts_dir)).unwrap();

    if let Err(err) = result {
        panic::resume_unwind(err);
    }
}

#[test]
fn allow_origin_toml() {
    fn check(text: &str, allow_origin: AllowOrigin) {
        #[derive(Serialize, Deserialize)]
        struct Config {
            allow_origin: AllowOrigin,
        }
        let config_toml = format!("allow_origin = {}\n", text);
        let config: Config = ::toml::from_str(&config_toml).unwrap();
        assert_eq!(config.allow_origin, allow_origin);
        assert_eq!(::toml::to_string(&config).unwrap(), config_toml);
    }

    check(r#""*""#, AllowOrigin::Any);
    check(
        r#""http://example.com""#,
        AllowOrigin::Whitelist(vec!["http://example.com".to_string()]),
    );
    check(
        r#"["http://a.org", "http://b.org"]"#,
        AllowOrigin::Whitelist(vec!["http://a.org".to_string(), "http://b.org".to_string()]),
    );
}

#[test]
fn test_update_config() {
    const TEST_DIR: &str = "config-update";
    const TEST_CONFIG_FILE: &str = "config01.toml";

    let full_test_dir = full_tmp_folder(TEST_DIR);
    fs::create_dir_all(Path::new(&full_test_dir))
        .expect("Expected test temp folder to be created.");

    // Copy test config to the separate file.
    let config_path = full_tmp_name(TEST_CONFIG_FILE, TEST_DIR);
    let testdata_path = full_testdata_name(TEST_CONFIG_FILE);
    fs::copy(testdata_path, config_path.clone()).unwrap();

    // Test config update.
    let peer = ConnectInfo {
        address: SocketAddr::from_str("0.0.0.1:8080").unwrap(),
        public_key: PublicKey::new([1; PUBLIC_KEY_LENGTH]),
    };

    let connect_list = ConnectListConfig { peers: vec![peer] };

    ConfigManager::update_connect_list(connect_list.clone(), &config_path)
        .expect("Unable to update connect list");
    let config: NodeConfig =
        ConfigFile::load(config_path.clone()).expect("Can't load node config file");

    let new_connect_list = config.connect_list;
    assert_eq!(new_connect_list.peers, connect_list.peers);

    // Cleanup.
    fs::remove_dir_all(Path::new(&full_test_dir)).unwrap();
}

#[test]
fn test_domain_name_peer() {
    const TEST_CONFIG_FILE: &str = "config_domain.toml";
    let testdata_path = full_testdata_name(TEST_CONFIG_FILE);
    let config: ConnectListConfig =
        ConfigFile::load(testdata_path).expect("Can't load node config file");

    let resolve_address = |a: &str| {
        a.to_socket_addrs()
            .expect(&format!("unable to resolve name from hostname: {}", a))
            .next()
            .expect(&format!("no one ip belongs to the hostname: {}", a))
    };

    let connect_info = match resolve_address("localhost:6333") {
        SocketAddr::V4(..) => ConnectInfo {
            address: "127.0.0.1:6333".parse().unwrap(),
            public_key: PublicKey::from_hex(
                "648e98a2405a40325d946bf8de6937795fe5c22ab095bca765a8b218e49ff5a3",
            ).unwrap(),
        },
        SocketAddr::V6(..) => ConnectInfo {
            address: "[::1]:6333".parse().unwrap(),
            public_key: PublicKey::from_hex(
                "648e98a2405a40325d946bf8de6937795fe5c22ab095bca765a8b218e49ff5a3",
            ).unwrap(),
        },
    };

    let connect_list = ConnectListConfig {
        peers: vec![
            ConnectInfo {
                address: "127.0.0.1:6333".parse().unwrap(),
                public_key: PublicKey::from_hex(
                    "16ef83ca4b231404daec6d07b24beb84d89c25944285d2e32a2dcf8f0f3eda72",
                ).unwrap(),
            },
            connect_info,
            ConnectInfo {
                address: "94.130.16.228:6333".parse().unwrap(),
                public_key: PublicKey::from_hex(
                    "924625eb77b9ad21e76713e7ada715945fbf0a926698832e121484c797fcc58e",
                ).unwrap(),
            },
        ],
    };

    assert_eq!(config.peers, connect_list.peers);
}
<|endoftext|>