<commit_before>//! OpenGL back-end for Rust-Graphics.

// External crates.
use graphics::BackEnd;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

// Local crate.
use Texture;

static VERTEX_SHADER_TRI_LIST_XY_RGBA: &'static str = "
#version 330
in vec4 a_v4Position;
in vec4 a_v4FillColor;

out vec4 v_v4FillColor;

void main()
{
    v_v4FillColor = a_v4FillColor;
    gl_Position = a_v4Position;
}
";

static FRAGMENT_SHADER_TRI_LIST_XY_RGBA: &'static str = "
#version 330
out vec4 out_color;
in vec4 v_v4FillColor;

void main()
{
    out_color = v_v4FillColor;
}
";

static VERTEX_SHADER_TRI_LIST_XY_RGBA_UV: &'static str = "
#version 330
in vec4 a_v4Position;
in vec4 a_v4FillColor;
in vec2 a_v2TexCoord;

uniform sampler2D s_texture;

out vec2 v_v2TexCoord;
out vec4 v_v4FillColor;

void main()
{
    v_v2TexCoord = a_v2TexCoord;
    v_v4FillColor = a_v4FillColor;
    gl_Position = a_v4Position;
}
";

static FRAGMENT_SHADER_TRI_LIST_XY_RGBA_UV: &'static str = "
#version 330
out vec4 out_color;

uniform sampler2D s_texture;

in vec2 v_v2TexCoord;
in vec4 v_v4FillColor;

void main()
{
    out_color = texture(s_texture, v_v2TexCoord) * v_v4FillColor;
}
";

struct TriListXYRGBA {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    a_v4Position: DynamicAttribute,
    a_v4FillColor: DynamicAttribute,
}

impl Drop for TriListXYRGBA {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl TriListXYRGBA {
    fn new() -> TriListXYRGBA {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_TRI_LIST_XY_RGBA      // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_TRI_LIST_XY_RGBA    // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let program = gl::CreateProgram();

        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let a_v4Position = DynamicAttribute::xy(
                program,
                "a_v4Position",
                vao
            ).unwrap();
        let a_v4FillColor = DynamicAttribute::rgba(
                program,
                "a_v4FillColor",
                vao
            ).unwrap();
        TriListXYRGBA {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            a_v4Position: a_v4Position,
            a_v4FillColor: a_v4FillColor,
        }
    }
}

struct TriListXYRGBAUV {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    a_v4Position: DynamicAttribute,
    a_v4FillColor: DynamicAttribute,
    a_v2TexCoord: DynamicAttribute,
}

impl Drop for TriListXYRGBAUV {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl TriListXYRGBAUV {
    fn new() -> TriListXYRGBAUV {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_TRI_LIST_XY_RGBA_UV   // shader type
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_TRI_LIST_XY_RGBA_UV // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };

        let program = gl::CreateProgram();
        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let a_v4Position = DynamicAttribute::xy(
                program,
                "a_v4Position",
                vao
            ).unwrap();
        let a_v4FillColor = DynamicAttribute::rgba(
                program,
                "a_v4FillColor",
                vao
            ).unwrap();
        let a_v2TexCoord = DynamicAttribute::uv(
                program,
                "a_v2TexCoord",
                vao
            ).unwrap();
        TriListXYRGBAUV {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            a_v4Position: a_v4Position,
            a_v4FillColor: a_v4FillColor,
            a_v2TexCoord: a_v2TexCoord,
        }
    }
}

/// Contains OpenGL data.
pub struct Gl {
    tri_list_xy_rgba: TriListXYRGBA,
    tri_list_xy_rgba_uv: TriListXYRGBAUV,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
}

impl<'a> Gl {
    /// Creates a new OpenGl back-end.
    pub fn new() -> Gl {
        // Load the vertices, color and texture coord buffers.
        Gl {
            tri_list_xy_rgba: TriListXYRGBA::new(),
            tri_list_xy_rgba_uv: TriListXYRGBAUV::new(),
            current_program: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return; }
            },
        }

        gl::UseProgram(program);
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }
}

impl BackEnd<Texture> for Gl {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        gl::ClearColor(r, g, b, a);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
    }

    fn enable_alpha_blend(&mut self) {
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    fn disable_alpha_blend(&mut self) {
        gl::Disable(gl::BLEND);
    }

    fn supports_single_texture(&self) -> bool { true }

    fn enable_single_texture(&mut self, texture: &Texture) {
        let texture = texture.get_id();
        gl::BindTexture(gl::TEXTURE_2D, texture);
    }

    fn disable_single_texture(&mut self) {}

    // Assume all textures has alpha channel for now.
    fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }

    fn supports_tri_list_xy_f32_rgba_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.tri_list_xy_rgba.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.tri_list_xy_rgba;
        gl::BindVertexArray(shader.vao);

        // xy makes two floats.
        let size_vertices: i32 = 2;

        unsafe {
            shader.a_v4Position.set(vertices);
            shader.a_v4FillColor.set(colors);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }

    fn supports_tri_list_xy_f32_rgba_f32_uv_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.tri_list_xy_rgba_uv.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.tri_list_xy_rgba_uv;
        gl::BindVertexArray(shader.vao);

        let size_vertices: i32 = 2;

        unsafe {
            shader.a_v4Position.set(vertices);
            shader.a_v4FillColor.set(colors);
            shader.a_v2TexCoord.set(texture_coords);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }
}

<commit_msg>Simplified shader parameters<commit_after>//! OpenGL back-end for Rust-Graphics.

// External crates.
use graphics::BackEnd;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

// Local crate.
use Texture;

static VERTEX_SHADER_TRI_LIST_XY_RGBA: &'static str = "
#version 330
in vec4 pos;
in vec4 color;

out vec4 v_color;

void main()
{
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_TRI_LIST_XY_RGBA: &'static str = "
#version 330
out vec4 out_color;
in vec4 v_color;

void main()
{
    out_color = v_color;
}
";

static VERTEX_SHADER_TRI_LIST_XY_RGBA_UV: &'static str = "
#version 330
in vec4 pos;
in vec4 color;
in vec2 uv;

uniform sampler2D s_texture;

out vec2 v_v2TexCoord;
out vec4 v_color;

void main()
{
    v_v2TexCoord = uv;
    v_color = color;
    gl_Position = pos;
}
";

static FRAGMENT_SHADER_TRI_LIST_XY_RGBA_UV: &'static str = "
#version 330
out vec4 out_color;

uniform sampler2D s_texture;

in vec2 v_v2TexCoord;
in vec4 v_color;

void main()
{
    out_color = texture(s_texture, v_v2TexCoord) * v_color;
}
";

struct TriListXYRGBA {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
}

impl Drop for TriListXYRGBA {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl TriListXYRGBA {
    fn new() -> TriListXYRGBA {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_TRI_LIST_XY_RGBA      // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_TRI_LIST_XY_RGBA    // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let program = gl::CreateProgram();

        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        TriListXYRGBA {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
        }
    }
}

struct TriListXYRGBAUV {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    pos: DynamicAttribute,
    color: DynamicAttribute,
    uv: DynamicAttribute,
}

impl Drop for TriListXYRGBAUV {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
        }
        gl::DeleteProgram(self.program);
        gl::DeleteShader(self.vertex_shader);
        gl::DeleteShader(self.fragment_shader);
    }
}

impl TriListXYRGBAUV {
    fn new() -> TriListXYRGBAUV {
        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            VERTEX_SHADER_TRI_LIST_XY_RGBA_UV   // shader type
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            FRAGMENT_SHADER_TRI_LIST_XY_RGBA_UV // shader source
        ) {
            Ok(id) => id,
            Err(s) => fail!("compile_shader: {}", s)
        };

        let program = gl::CreateProgram();
        gl::AttachShader(program, vertex_shader);
        gl::AttachShader(program, fragment_shader);

        unsafe {
            "out_color".with_c_str(
                |ptr| gl::BindFragDataLocation(program, 0, ptr)
            );
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
        }
        gl::LinkProgram(program);
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let color = DynamicAttribute::rgba(
                program,
                "color",
                vao
            ).unwrap();
        let uv = DynamicAttribute::uv(
                program,
                "uv",
                vao
            ).unwrap();
        TriListXYRGBAUV {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
            uv: uv,
        }
    }
}

/// Contains OpenGL data.
pub struct Gl {
    tri_list_xy_rgba: TriListXYRGBA,
    tri_list_xy_rgba_uv: TriListXYRGBAUV,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
}

impl<'a> Gl {
    /// Creates a new OpenGl back-end.
    pub fn new() -> Gl {
        // Load the vertices, color and texture coord buffers.
        Gl {
            tri_list_xy_rgba: TriListXYRGBA::new(),
            tri_list_xy_rgba_uv: TriListXYRGBAUV::new(),
            current_program: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return; }
            },
        }

        gl::UseProgram(program);
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }
}

impl BackEnd<Texture> for Gl {
    fn supports_clear_rgba(&self) -> bool { true }

    fn clear_rgba(&mut self, r: f32, g: f32, b: f32, a: f32) {
        gl::ClearColor(r, g, b, a);
        gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
    }

    fn enable_alpha_blend(&mut self) {
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    fn disable_alpha_blend(&mut self) {
        gl::Disable(gl::BLEND);
    }

    fn supports_single_texture(&self) -> bool { true }

    fn enable_single_texture(&mut self, texture: &Texture) {
        let texture = texture.get_id();
        gl::BindTexture(gl::TEXTURE_2D, texture);
    }

    fn disable_single_texture(&mut self) {}

    // Assume all textures has alpha channel for now.
    fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }

    fn supports_tri_list_xy_f32_rgba_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.tri_list_xy_rgba.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.tri_list_xy_rgba;
        gl::BindVertexArray(shader.vao);

        // xy makes two floats.
        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }

    fn supports_tri_list_xy_f32_rgba_f32_uv_f32(&self) -> bool { true }

    fn tri_list_xy_f32_rgba_f32_uv_f32(
        &mut self,
        vertices: &[f32],
        colors: &[f32],
        texture_coords: &[f32]
    ) {
        {
            // Set shader program.
            let shader_program = self.tri_list_xy_rgba_uv.program;
            self.use_program(shader_program);
        }
        let ref mut shader = self.tri_list_xy_rgba_uv;
        gl::BindVertexArray(shader.vao);

        let size_vertices: i32 = 2;

        unsafe {
            shader.pos.set(vertices);
            shader.color.set(colors);
            shader.uv.set(texture_coords);
        }

        // Render triangles whether they are facing
        // clockwise or counter clockwise.
        gl::CullFace(gl::FRONT_AND_BACK);

        let items: i32 = vertices.len() as i32 / size_vertices;
        gl::DrawArrays(gl::TRIANGLES, 0, items);

        gl::BindVertexArray(0);
    }
}

<|endoftext|>


<commit_before>use crate::hir::{self, Ast};
use crate::parser::ast;
use crate::util::fmap;

use super::monomorphisation::Context;

impl<'c> Context<'c> {
    /// Find all recursive calls of this closure and change the environment
    /// to be the parameter environment rather than that of the callsite.
    ///
    /// If the given argument is not a closure this will do nothing
    pub fn fix_recursive_closure_calls(
        &mut self, expr: Ast, definition: &ast::Definition<'c>, definition_id: hir::DefinitionId,
    ) -> Ast {
        match definition.expr.as_ref() {
            ast::Ast::Lambda(lambda) if !lambda.closure_environment.is_empty() => {
                let (f, outer_env) = unwrap_closure(expr);
                let inner_env = get_env_parameter(&f);
                let inner_f = self.fresh_variable();

                let new_f = replace_env(f, &inner_env, definition_id, &inner_f);
                let def = Ast::Definition(hir::Definition {
                    variable: inner_f.definition_id,
                    name: None,
                    expr: Box::new(new_f),
                });

                let seq = Ast::Sequence(hir::Sequence { statements: vec![def, inner_f.into()] });
                Ast::Tuple(hir::Tuple { fields: vec![seq, outer_env] })
            },
            _ => expr,
        }
    }
}

/// Given a closure, return (f, env)
fn unwrap_closure(expr: Ast) -> (Ast, Ast) {
    match expr {
        Ast::Tuple(mut elems) => {
            // Extract the env parameter
            assert_eq!(elems.fields.len(), 2);
            let env = elems.fields.pop().unwrap();
            let f = elems.fields.pop().unwrap();
            (f, env)
        },
        other => unreachable!("Expected a closure tuple, found:\n  {}", other),
    }
}

fn get_env_parameter(expr: &Ast) -> Ast {
    // A closure's environment is always its last argument
    match expr {
        Ast::Lambda(lambda) => lambda.args.last().unwrap().clone().into(),
        other => unreachable!("Expected a lambda within the closure, found:\n  {}", other),
    }
}

fn replace_env(expr: Ast, env: &Ast, definition_id: hir::DefinitionId, f: &hir::Variable) -> Ast {
    match expr {
        Ast::Variable(var) if var.definition_id == definition_id => {
            let env = env.clone();
            let new_var = Ast::Variable(f.clone());
            Ast::Tuple(hir::Tuple { fields: vec![new_var, env] })
        },
        Ast::Literal(_) => expr,
        Ast::Variable(_) => expr,
        Ast::Lambda(mut lambda) => {
            lambda.body = Box::new(replace_env(*lambda.body, env, definition_id, f));
            Ast::Lambda(lambda)
        },
        Ast::FunctionCall(mut call) => {
            call.function = Box::new(replace_env(*call.function, env, definition_id, f));
            call.args = fmap(call.args, |arg| replace_env(arg, env, definition_id, f));
            Ast::FunctionCall(call)
        },
        Ast::Definition(mut def) => {
            def.expr = Box::new(replace_env(*def.expr, env, definition_id, f));
            Ast::Definition(def)
        },
        Ast::If(mut if_expr) => {
            if_expr.condition = Box::new(replace_env(*if_expr.condition, env, definition_id, f));
            if_expr.then = Box::new(replace_env(*if_expr.then, env, definition_id, f));
            if let Some(otherwise) = if_expr.otherwise {
                if_expr.otherwise = Some(Box::new(replace_env(*otherwise, env, definition_id, f)));
            }
            Ast::If(if_expr)
        },
        Ast::Sequence(mut seq) => {
            seq.statements = fmap(seq.statements, |stmt| replace_env(stmt, env, definition_id, f));
            Ast::Sequence(seq)
        },
        Ast::MemberAccess(mut access) => {
            access.lhs = Box::new(replace_env(*access.lhs, env, definition_id, f));
            Ast::MemberAccess(access)
        },
        Ast::Tuple(mut tuple) => {
            tuple.fields = fmap(tuple.fields, |field| replace_env(field, env, definition_id, f));
            Ast::Tuple(tuple)
        },
        Ast::Match(_) => todo!(),
        Ast::Return(_) => todo!(),
        Ast::Extern(_) => todo!(),
        Ast::Assignment(_) => todo!(),
        Ast::ReinterpretCast(_) => todo!(),
        Ast::Builtin(_) => todo!(),
    }
}
<commit_msg>Fill in todos<commit_after>use crate::hir::{self, Ast};
use crate::parser::ast;
use crate::util::fmap;

use super::monomorphisation::Context;
use super::DecisionTree;

impl<'c> Context<'c> {
    /// Find all recursive calls of this closure and change the environment
    /// to be the parameter environment rather than that of the callsite.
    ///
    /// If the given argument is not a closure this will do nothing
    pub fn fix_recursive_closure_calls(
        &mut self, expr: Ast, definition: &ast::Definition<'c>, definition_id: hir::DefinitionId,
    ) -> Ast {
        match definition.expr.as_ref() {
            ast::Ast::Lambda(lambda) if !lambda.closure_environment.is_empty() => {
                let (f, outer_env) = unwrap_closure(expr);
                let inner_env = get_env_parameter(&f);
                let inner_f = self.fresh_variable();

                let new_f = replace_env(f, &inner_env, definition_id, &inner_f);
                let def = Ast::Definition(hir::Definition {
                    variable: inner_f.definition_id,
                    name: None,
                    expr: Box::new(new_f),
                });

                let seq = Ast::Sequence(hir::Sequence { statements: vec![def, inner_f.into()] });
                Ast::Tuple(hir::Tuple { fields: vec![seq, outer_env] })
            },
            _ => expr,
        }
    }
}

/// Given a closure, return (f, env)
fn unwrap_closure(expr: Ast) -> (Ast, Ast) {
    match expr {
        Ast::Tuple(mut elems) => {
            // Extract the env parameter
            assert_eq!(elems.fields.len(), 2);
            let env = elems.fields.pop().unwrap();
            let f = elems.fields.pop().unwrap();
            (f, env)
        },
        other => unreachable!("Expected a closure tuple, found:\n  {}", other),
    }
}

fn get_env_parameter(expr: &Ast) -> Ast {
    // A closure's environment is always its last argument
    match expr {
        Ast::Lambda(lambda) => lambda.args.last().unwrap().clone().into(),
        other => unreachable!("Expected a lambda within the closure, found:\n  {}", other),
    }
}

fn replace_env(expr: Ast, env: &Ast, definition_id: hir::DefinitionId, f: &hir::Variable) -> Ast {
    match expr {
        Ast::Variable(var) if var.definition_id == definition_id => {
            let env = env.clone();
            let new_var = Ast::Variable(f.clone());
            Ast::Tuple(hir::Tuple { fields: vec![new_var, env] })
        },
        Ast::Literal(_) => expr,
        Ast::Variable(_) => expr,
        Ast::Extern(_) => expr,
        Ast::Lambda(mut lambda) => {
            lambda.body = Box::new(replace_env(*lambda.body, env, definition_id, f));
            Ast::Lambda(lambda)
        },
        Ast::FunctionCall(mut call) => {
            call.function = Box::new(replace_env(*call.function, env, definition_id, f));
            call.args = fmap(call.args, |arg| replace_env(arg, env, definition_id, f));
            Ast::FunctionCall(call)
        },
        Ast::Definition(mut def) => {
            def.expr = Box::new(replace_env(*def.expr, env, definition_id, f));
            Ast::Definition(def)
        },
        Ast::If(mut if_expr) => {
            if_expr.condition = Box::new(replace_env(*if_expr.condition, env, definition_id, f));
            if_expr.then = Box::new(replace_env(*if_expr.then, env, definition_id, f));
            if let Some(otherwise) = if_expr.otherwise {
                if_expr.otherwise = Some(Box::new(replace_env(*otherwise, env, definition_id, f)));
            }
            Ast::If(if_expr)
        },
        Ast::Sequence(mut seq) => {
            seq.statements = fmap(seq.statements, |stmt| replace_env(stmt, env, definition_id, f));
            Ast::Sequence(seq)
        },
        Ast::MemberAccess(mut access) => {
            *access.lhs = replace_env(*access.lhs, env, definition_id, f);
            Ast::MemberAccess(access)
        },
        Ast::Tuple(mut tuple) => {
            tuple.fields = fmap(tuple.fields, |field| replace_env(field, env, definition_id, f));
            Ast::Tuple(tuple)
        },
        Ast::Assignment(mut assignment) => {
            assignment.lhs = Box::new(replace_env(*assignment.lhs, env, definition_id, f));
            assignment.rhs = Box::new(replace_env(*assignment.rhs, env, definition_id, f));
            Ast::Assignment(assignment)
        },
        Ast::Match(mut match_expr) => {
            match_expr.branches = fmap(match_expr.branches, |branch| replace_env(branch, env, definition_id, f));
            match_expr.decision_tree = replace_env_decision_tree(match_expr.decision_tree, env, definition_id, f);
            Ast::Match(match_expr)
        },
        Ast::Return(mut ret) => {
            *ret.expression = replace_env(*ret.expression, env, definition_id, f);
            Ast::Return(ret)
        },
        Ast::ReinterpretCast(mut cast) => {
            *cast.lhs = replace_env(*cast.lhs, env, definition_id, f);
            Ast::ReinterpretCast(cast)
        },
        Ast::Builtin(builtin) => Ast::Builtin(replace_env_builtin(builtin, env, definition_id, f)),
    }
}

fn replace_env_decision_tree(
    expr: DecisionTree, env: &Ast, definition_id: hir::DefinitionId, f: &hir::Variable,
) -> DecisionTree {
    match expr {
        DecisionTree::Leaf(_) => expr,
        DecisionTree::Definition(mut definition, tree) => {
            definition.expr = Box::new(replace_env(*definition.expr, env, definition_id, f));
            let tree = Box::new(replace_env_decision_tree(*tree, env, definition_id, f));
            DecisionTree::Definition(definition, tree)
        },
        DecisionTree::Switch { int_to_switch_on, cases, else_case } => {
            let int_to_switch_on = Box::new(replace_env(*int_to_switch_on, env, definition_id, f));
            let cases = fmap(cases, |(tag, case)| {
                let case = replace_env_decision_tree(case, env, definition_id, f);
                (tag, case)
            });
            let else_case = else_case.map(|case| Box::new(replace_env_decision_tree(*case, env, definition_id, f)));
            DecisionTree::Switch { int_to_switch_on, cases, else_case }
        },
    }
}

fn replace_env_builtin(
    builtin: hir::Builtin, env: &Ast, definition_id: hir::DefinitionId, f: &hir::DefinitionInfo,
) -> hir::Builtin {
    use hir::Builtin::*;

    let f = |mut ast: Box<Ast>| {
        *ast = replace_env(*ast, env, definition_id, f);
        ast
    };

    // Nothing special, just recur on all Asts
    match builtin {
        AddInt(lhs, rhs) => AddInt(f(lhs), f(rhs)),
        AddFloat(lhs, rhs) => AddFloat(f(lhs), f(rhs)),
        SubInt(lhs, rhs) => SubInt(f(lhs), f(rhs)),
        SubFloat(lhs, rhs) => SubFloat(f(lhs), f(rhs)),
        MulInt(lhs, rhs) => MulInt(f(lhs), f(rhs)),
        MulFloat(lhs, rhs) => MulFloat(f(lhs), f(rhs)),
        DivSigned(lhs, rhs) => DivSigned(f(lhs), f(rhs)),
        DivUnsigned(lhs, rhs) => DivUnsigned(f(lhs), f(rhs)),
        DivFloat(lhs, rhs) => DivFloat(f(lhs), f(rhs)),
        ModSigned(lhs, rhs) => ModSigned(f(lhs), f(rhs)),
        ModUnsigned(lhs, rhs) => ModUnsigned(f(lhs), f(rhs)),
        ModFloat(lhs, rhs) => ModFloat(f(lhs), f(rhs)),
        LessSigned(lhs, rhs) => LessSigned(f(lhs), f(rhs)),
        LessUnsigned(lhs, rhs) => LessUnsigned(f(lhs), f(rhs)),
        LessFloat(lhs, rhs) => LessFloat(f(lhs), f(rhs)),
        EqInt(lhs, rhs) => EqInt(f(lhs), f(rhs)),
        EqFloat(lhs, rhs) => EqFloat(f(lhs), f(rhs)),
        EqChar(lhs, rhs) => EqChar(f(lhs), f(rhs)),
        EqBool(lhs, rhs) => EqBool(f(lhs), f(rhs)),
        SignExtend(lhs, t) => SignExtend(f(lhs), t),
        ZeroExtend(lhs, t) => ZeroExtend(f(lhs), t),
        SignedToFloat(lhs, t) => SignedToFloat(f(lhs), t),
        UnsignedToFloat(lhs, t) => UnsignedToFloat(f(lhs), t),
        FloatToSigned(lhs, t) => FloatToSigned(f(lhs), t),
        FloatToUnsigned(lhs, t) => FloatToUnsigned(f(lhs), t),
        BitwiseAnd(lhs, rhs) => BitwiseAnd(f(lhs), f(rhs)),
        BitwiseOr(lhs, rhs) => BitwiseOr(f(lhs), f(rhs)),
        BitwiseXor(lhs, rhs) => BitwiseXor(f(lhs), f(rhs)),
        BitwiseNot(lhs) => BitwiseNot(f(lhs)),
        Truncate(lhs, t) => Truncate(f(lhs), t),
        Deref(lhs, t) => Deref(f(lhs), t),
        Offset(lhs, rhs, size) => Offset(f(lhs), f(rhs), size),
        Transmute(lhs, t) => Transmute(f(lhs), t),
        StackAlloc(lhs) => StackAlloc(f(lhs)),
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate tokio_core;
extern crate futures;

use std::sync::{Arc};
use std::thread;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

use self::tokio_core::reactor::Core;
use self::tokio_core::net::TcpListener;
use self::hyper::server::Http;
use self::futures::stream::Stream;
use self::futures::Future;

use error::Error;
use config::Config;
use http::server::hyper::Chunk;
use http::dispatcher::Dispatcher;
use http::app_manager::AppManager;

pub struct Server {
    pub thread: thread::JoinHandle<()>,
}

impl Server {
    pub fn create(config: &Config) -> Result<Server, Error> {
        let app_manager = Arc::new(AppManager::new()?);

        let thread = Server::create_thread(app_manager, config.http_port);

        Ok(Server {
            thread: thread,
        })
    }

    fn create_thread(app_manager: Arc<AppManager>, port: u16) -> thread::JoinHandle<()> {
        thread::spawn(move || {
            let http = Http::<Chunk>::new();
            let mut core = Core::new().unwrap();
            let handle = core.handle();

            let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);

            let listener = TcpListener::bind(&address, &handle).unwrap();

            let server = listener.incoming().for_each(|(sock, _)| {
                let service = Dispatcher::new(app_manager.clone(), handle.clone());
                let conn = http.serve_connection(sock, service);
                let fut = conn
                    .map(|_| ())
                    .map_err(|e| eprintln!("server connection error: {}", e));
                handle.spawn(fut);
                Ok(())
            });

            core.run(server).unwrap();
        })
    }
}

<commit_msg>refactor: Rename variable<commit_after>extern crate hyper;
extern crate tokio_core;
extern crate futures;

use std::sync::{Arc};
use std::thread;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

use self::tokio_core::reactor::Core;
use self::tokio_core::net::TcpListener;
use self::hyper::server::Http;
use self::futures::stream::Stream;
use self::futures::Future;

use error::Error;
use config::Config;
use http::server::hyper::Chunk;
use http::dispatcher::Dispatcher;
use http::app_manager::AppManager;

pub struct Server {
    pub thread: thread::JoinHandle<()>,
}

impl Server {
    pub fn create(config: &Config) -> Result<Server, Error> {
        let app_manager = Arc::new(AppManager::new()?);

        let thread = Server::create_thread(app_manager, config.http_port);

        Ok(Server {
            thread: thread,
        })
    }

    fn create_thread(app_manager: Arc<AppManager>, port: u16) -> thread::JoinHandle<()> {
        thread::spawn(move || {
            let http = Http::<Chunk>::new();
            let mut core = Core::new().unwrap();
            let handle = core.handle();

            let address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port);

            let listener = TcpListener::bind(&address, &handle).unwrap();

            let server = listener.incoming().for_each(|(tcp_stream, _)| {
                let service = Dispatcher::new(app_manager.clone(), handle.clone());
                let conn = http.serve_connection(tcp_stream, service);
                let fut = conn
                    .map(|_| ())
                    .map_err(|e| eprintln!("server connection error: {}", e));
                handle.spawn(fut);
                Ok(())
            });

            core.run(server).unwrap();
        })
    }
}

<|endoftext|>


<commit_before>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use libnum::Zero;
use itertools::free::enumerate;

use imp_prelude::*;
use numeric_util;

use {
    LinalgScalar,
};

#[cfg(feature="blas")]
use std::mem::swap;
#[cfg(feature="blas")]
use std::os::raw::c_int;
#[cfg(feature="blas")]
use std::any::{Any, TypeId};

#[cfg(feature="blas")]
use blas_sys::c::{CblasNoTrans, CblasTrans, CblasRowMajor};
#[cfg(feature="blas")]
use blas_sys;

/// len of vector before we use blas
#[cfg(feature="blas")]
const DOT_BLAS_CUTOFF: usize = 32;
/// side of matrix before we use blas
#[cfg(feature="blas")]
const GEMM_BLAS_CUTOFF: usize = 7;

impl<A, S> ArrayBase<S, Ix>
    where S: Data<Elem=A>,
{
    /// Compute the dot product of one-dimensional arrays.
    ///
    /// The dot product is a sum of the elementwise products (no conjugation
    /// of complex operands, and thus not their inner product).
    ///
    /// **Panics** if the arrays are not of the same length.
    pub fn dot<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        self.dot_impl(rhs)
    }

    fn dot_generic<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        debug_assert_eq!(self.len(), rhs.len());
        assert!(self.len() == rhs.len());
        if let Some(self_s) = self.as_slice() {
            if let Some(rhs_s) = rhs.as_slice() {
                return numeric_util::unrolled_dot(self_s, rhs_s);
            }
        }
        let mut sum = A::zero();
        for i in 0..self.len() {
            unsafe {
                sum = sum.clone() + self.uget(i).clone() * rhs.uget(i).clone();
            }
        }
        sum
    }

    #[cfg(not(feature="blas"))]
    fn dot_impl<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        self.dot_generic(rhs)
    }

    #[cfg(feature="blas")]
    fn dot_impl<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        // Use only if the vector is large enough to be worth it
        if self.len() >= DOT_BLAS_CUTOFF {
            debug_assert_eq!(self.len(), rhs.len());
            assert!(self.len() == rhs.len());
            macro_rules! dot {
                ($ty:ty, $func:ident) => {{
            if blas_compat_1d::<$ty, _>(self) && blas_compat_1d::<$ty, _>(rhs) {
                let n = self.len() as c_int;
                let incx = self.strides()[0] as c_int;
                let incy = rhs.strides()[0] as c_int;
                let ret = unsafe {
                    blas_sys::c::$func(
                        n,
                        self.ptr as *const $ty,
                        incx,
                        rhs.ptr as *const $ty,
                        incy)
                };
                return cast_as::<$ty, A>(&ret);
            }
                }}
            }

            dot!{f32, cblas_sdot};
            dot!{f64, cblas_ddot};
        }
        self.dot_generic(rhs)
    }
}

impl<A, S> ArrayBase<S, (Ix, Ix)>
    where S: Data<Elem=A>,
{
    /// Return an array view of row `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn row(&self, index: Ix) -> ArrayView<A, Ix>
    {
        self.subview(Axis(0), index)
    }

    /// Return a mutable array view of row `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn row_mut(&mut self, index: Ix) -> ArrayViewMut<A, Ix>
        where S: DataMut
    {
        self.subview_mut(Axis(0), index)
    }

    /// Return an array view of column `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn column(&self, index: Ix) -> ArrayView<A, Ix>
    {
        self.subview(Axis(1), index)
    }

    /// Return a mutable array view of column `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn column_mut(&mut self, index: Ix) -> ArrayViewMut<A, Ix>
        where S: DataMut
    {
        self.subview_mut(Axis(1), index)
    }

    /// Perform matrix multiplication of rectangular arrays `self` and `rhs`.
    ///
    /// The array shapes must agree in the way that
    /// if `self` is *M* × *N*, then `rhs` is *N* × *K*.
    ///
    /// Return a result array with shape *M* × *K*.
    ///
    /// **Panics** if shapes are incompatible.
    ///
    /// ```
    /// use ndarray::arr2;
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [0., 1.]]);
    /// let b = arr2(&[[1., 2.],
    ///                [2., 3.]]);
    ///
    /// assert!(
    ///     a.mat_mul(&b) == arr2(&[[5., 8.],
    ///                             [2., 3.]])
    /// );
    /// ```
    ///
    pub fn mat_mul(&self, rhs: &ArrayBase<S, (Ix, Ix)>) -> OwnedArray<A, (Ix, Ix)>
        where A: LinalgScalar,
    {
        let ((m, a), (b, n)) = (self.dim, rhs.dim);
        let (lhs_columns, rhs_rows) = (a, b);
        assert!(lhs_columns == rhs_rows);
        assert!(m.checked_mul(n).is_some());

        mat_mul_impl(self, rhs)
    }

    /// Perform the matrix multiplication of the rectangular array `self` and
    /// column vector `rhs`.
    ///
    /// The array shapes must agree in the way that
    /// if `self` is *M* × *N*, then `rhs` is *N*.
    ///
    /// Return a result array with shape *M*.
    ///
    /// **Panics** if shapes are incompatible.
    pub fn mat_mul_col(&self, rhs: &ArrayBase<S, Ix>) -> OwnedArray<A, Ix>
        where A: LinalgScalar,
    {
        let ((m, a), n) = (self.dim, rhs.dim);
        let (self_columns, other_rows) = (a, n);
        assert!(self_columns == other_rows);

        // Avoid initializing the memory in vec -- set it during iteration
        let mut res_elems = Vec::<A>::with_capacity(m as usize);
        unsafe {
            res_elems.set_len(m as usize);
        }
        for (i, rr) in enumerate(&mut res_elems) {
            unsafe {
                *rr = (0..a).fold(A::zero(),
                    move |s, k| s + *self.uget((i, k)) * *rhs.uget(k)
                );
            }
        }
        unsafe {
            ArrayBase::from_vec_dim_unchecked(m, res_elems)
        }
    }
}

#[cfg(not(feature="blas"))]
use self::mat_mul_general as mat_mul_impl;

#[cfg(feature="blas")]
fn mat_mul_impl<A, S>(lhs: &ArrayBase<S, (Ix, Ix)>, rhs: &ArrayBase<S, (Ix, Ix)>)
    -> OwnedArray<A, (Ix, Ix)>
    where A: LinalgScalar,
          S: Data<Elem=A>,
{
    // size cutoff for using BLAS
    let cut = GEMM_BLAS_CUTOFF;
    let ((mut m, a), (_, mut n)) = (lhs.dim, rhs.dim);
    if !(m > cut || n > cut || a > cut) ||
        !(same_type::<A, f32>() || same_type::<A, f64>()) {
        return mat_mul_general(lhs, rhs);
    }
    // Use `c` for c-order and `f` for an f-order matrix
    // We can handle c * c, f * f generally and
    // c * f and f * c if the `f` matrix is square.
    let mut lhs_ = lhs.view();
    let mut rhs_ = rhs.view();
    let lhs_s0 = lhs_.strides()[0];
    let rhs_s0 = rhs_.strides()[0];
    let both_f = lhs_s0 == 1 && rhs_s0 == 1;
    let mut lhs_trans = CblasNoTrans;
    let mut rhs_trans = CblasNoTrans;
    if both_f {
        // A^t B^t = C^t => B A = C
        lhs_ = lhs_.reversed_axes();
        rhs_ = rhs_.reversed_axes();
        swap(&mut lhs_, &mut rhs_);
        swap(&mut m, &mut n);
    } else if lhs_s0 == 1 && m == a {
        lhs_ = lhs_.reversed_axes();
        lhs_trans = CblasTrans;
    } else if rhs_s0 == 1 && a == n {
        rhs_ = rhs_.reversed_axes();
        rhs_trans = CblasTrans;
    }

    macro_rules! gemm {
        ($ty:ty, $gemm:ident) => {
        if blas_row_major_2d::<$ty, _>(&lhs_) && blas_row_major_2d::<$ty, _>(&rhs_) {
            let mut elems = Vec::<A>::with_capacity(m * n);
            let c;
            unsafe {
                elems.set_len(m * n);
                c = OwnedArray::from_vec_dim_unchecked((m, n), elems);
            }
            {
                let (m, k) = match lhs_trans {
                    CblasNoTrans => lhs_.dim(),
                    _ => {
                        let (rows, cols) = lhs_.dim();
                        (cols, rows)
                    }
                };
                let n = match rhs_trans {
                    CblasNoTrans => rhs_.dim().1,
                    _ => rhs_.dim().0,
                };
                unsafe {
                    blas_sys::c::$gemm(
                    CblasRowMajor,
                    lhs_trans,
                    rhs_trans,
                    m as c_int, // m, rows of OP(a)
                    n as c_int, // n, cols of OP(b)
                    k as c_int, // k, cols of OP(a)
                    1.0,                  // alpha
                    lhs_.ptr as *const _, // a
                    lhs_.strides()[0] as c_int, // lda
                    rhs_.ptr as *const _, // b
                    rhs_.strides()[0] as c_int, // ldb
                    0.0,                   // beta
                    c.ptr as *mut _,       // c
                    c.strides()[0] as c_int, // ldc
                );
                }
            }
            return if both_f {
                c.reversed_axes()
            } else {
                c
            };
        }
        }
    }
    gemm!(f32, cblas_sgemm);
    gemm!(f64, cblas_dgemm);
    return mat_mul_general(lhs, rhs);
}

fn mat_mul_general<A, S>(lhs: &ArrayBase<S, (Ix, Ix)>, rhs: &ArrayBase<S, (Ix, Ix)>)
    -> OwnedArray<A, (Ix, Ix)>
    where A: LinalgScalar,
          S: Data<Elem=A>,
{
    let ((m, a), (_, n)) = (lhs.dim, rhs.dim);

    let lhs_s0 = lhs.strides()[0];
    let rhs_s0 = rhs.strides()[0];
    let column_major = lhs_s0 == 1 && rhs_s0 == 1;

    // Avoid initializing the memory in vec -- set it during iteration
    // Panic safe because A: Copy
    let mut res_elems = Vec::<A>::with_capacity(m as usize * n as usize);
    unsafe {
        res_elems.set_len(m as usize * n as usize);
    }
    let mut i = 0;
    let mut j = 0;
    for rr in &mut res_elems {
        unsafe {
            *rr = (0..a).fold(A::zero(),
                move |s, k| s + *lhs.uget((i, k)) * *rhs.uget((k, j)));
        }
        if !column_major {
            j += 1;
            if j == n {
                j = 0;
                i += 1;
            }
        } else {
            i += 1;
            if i == m {
                i = 0;
                j += 1;
            }
        }
    }
    unsafe {
        if !column_major {
            ArrayBase::from_vec_dim_unchecked((m, n), res_elems)
        } else {
            ArrayBase::from_vec_dim_unchecked_f((m, n), res_elems)
        }
    }
}

#[cfg(feature="blas")]
#[inline(always)]
/// Return `true` if `A` and `B` are the same type
fn same_type<A: Any, B: Any>() -> bool {
    TypeId::of::<A>() == TypeId::of::<B>()
}

#[cfg(feature="blas")]
// Read pointer to type `A` as type `B`.
//
// **Panics** if `A` and `B` are not the same type
fn cast_as<A: Any + Copy, B: Any + Copy>(a: &A) -> B {
    assert!(same_type::<A, B>());
    unsafe {
        ::std::ptr::read(a as *const _ as *const B)
    }
}

#[cfg(feature="blas")]
fn blas_compat_1d<A, S>(a: &ArrayBase<S, Ix>) -> bool
    where S: Data,
          A: Any,
          S::Elem: Any,
{
    if !same_type::<A, S::Elem>() {
        return false;
    }
    if a.len() > c_int::max_value() as usize {
        return false;
    }
    let stride = a.strides()[0];
    if stride > c_int::max_value() as isize ||
        stride < c_int::min_value() as isize {
        return false;
    }
    true
}

#[cfg(feature="blas")]
fn blas_row_major_2d<A, S>(a: &ArrayBase<S, (Ix, Ix)>) -> bool
    where S: Data,
          A: Any,
          S::Elem: Any,
{
    if !same_type::<A, S::Elem>() {
        return false;
    }
    let s0 = a.strides()[0];
    let s1 = a.strides()[1];
    if s1 != 1 {
        return false;
    }
    if (s0 > c_int::max_value() as isize || s0 < c_int::min_value() as isize) ||
        (s1 > c_int::max_value() as isize || s1 < c_int::min_value() as isize)
    {
        return false;
    }
    let (m, n) = a.dim();
    if m > c_int::max_value() as usize ||
        n > c_int::max_value() as usize
    {
        return false;
    }
    true
}
<commit_msg>Add comment for gemm<commit_after>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use libnum::Zero;
use itertools::free::enumerate;

use imp_prelude::*;
use numeric_util;

use {
    LinalgScalar,
};

#[cfg(feature="blas")]
use std::mem::swap;
#[cfg(feature="blas")]
use std::os::raw::c_int;
#[cfg(feature="blas")]
use std::any::{Any, TypeId};

#[cfg(feature="blas")]
use blas_sys::c::{CblasNoTrans, CblasTrans, CblasRowMajor};
#[cfg(feature="blas")]
use blas_sys;

/// len of vector before we use blas
#[cfg(feature="blas")]
const DOT_BLAS_CUTOFF: usize = 32;
/// side of matrix before we use blas
#[cfg(feature="blas")]
const GEMM_BLAS_CUTOFF: usize = 7;

impl<A, S> ArrayBase<S, Ix>
    where S: Data<Elem=A>,
{
    /// Compute the dot product of one-dimensional arrays.
    ///
    /// The dot product is a sum of the elementwise products (no conjugation
    /// of complex operands, and thus not their inner product).
    ///
    /// **Panics** if the arrays are not of the same length.
    pub fn dot<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        self.dot_impl(rhs)
    }

    fn dot_generic<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        debug_assert_eq!(self.len(), rhs.len());
        assert!(self.len() == rhs.len());
        if let Some(self_s) = self.as_slice() {
            if let Some(rhs_s) = rhs.as_slice() {
                return numeric_util::unrolled_dot(self_s, rhs_s);
            }
        }
        let mut sum = A::zero();
        for i in 0..self.len() {
            unsafe {
                sum = sum.clone() + self.uget(i).clone() * rhs.uget(i).clone();
            }
        }
        sum
    }

    #[cfg(not(feature="blas"))]
    fn dot_impl<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        self.dot_generic(rhs)
    }

    #[cfg(feature="blas")]
    fn dot_impl<S2>(&self, rhs: &ArrayBase<S2, Ix>) -> A
        where S2: Data<Elem=A>,
              A: LinalgScalar,
    {
        // Use only if the vector is large enough to be worth it
        if self.len() >= DOT_BLAS_CUTOFF {
            debug_assert_eq!(self.len(), rhs.len());
            assert!(self.len() == rhs.len());
            macro_rules! dot {
                ($ty:ty, $func:ident) => {{
            if blas_compat_1d::<$ty, _>(self) && blas_compat_1d::<$ty, _>(rhs) {
                let n = self.len() as c_int;
                let incx = self.strides()[0] as c_int;
                let incy = rhs.strides()[0] as c_int;
                let ret = unsafe {
                    blas_sys::c::$func(
                        n,
                        self.ptr as *const $ty,
                        incx,
                        rhs.ptr as *const $ty,
                        incy)
                };
                return cast_as::<$ty, A>(&ret);
            }
                }}
            }

            dot!{f32, cblas_sdot};
            dot!{f64, cblas_ddot};
        }
        self.dot_generic(rhs)
    }
}

impl<A, S> ArrayBase<S, (Ix, Ix)>
    where S: Data<Elem=A>,
{
    /// Return an array view of row `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn row(&self, index: Ix) -> ArrayView<A, Ix>
    {
        self.subview(Axis(0), index)
    }

    /// Return a mutable array view of row `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn row_mut(&mut self, index: Ix) -> ArrayViewMut<A, Ix>
        where S: DataMut
    {
        self.subview_mut(Axis(0), index)
    }

    /// Return an array view of column `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn column(&self, index: Ix) -> ArrayView<A, Ix>
    {
        self.subview(Axis(1), index)
    }

    /// Return a mutable array view of column `index`.
    ///
    /// **Panics** if `index` is out of bounds.
    pub fn column_mut(&mut self, index: Ix) -> ArrayViewMut<A, Ix>
        where S: DataMut
    {
        self.subview_mut(Axis(1), index)
    }

    /// Perform matrix multiplication of rectangular arrays `self` and `rhs`.
    ///
    /// The array shapes must agree in the way that
    /// if `self` is *M* × *N*, then `rhs` is *N* × *K*.
    ///
    /// Return a result array with shape *M* × *K*.
    ///
    /// **Panics** if shapes are incompatible.
    ///
    /// ```
    /// use ndarray::arr2;
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [0., 1.]]);
    /// let b = arr2(&[[1., 2.],
    ///                [2., 3.]]);
    ///
    /// assert!(
    ///     a.mat_mul(&b) == arr2(&[[5., 8.],
    ///                             [2., 3.]])
    /// );
    /// ```
    ///
    pub fn mat_mul(&self, rhs: &ArrayBase<S, (Ix, Ix)>) -> OwnedArray<A, (Ix, Ix)>
        where A: LinalgScalar,
    {
        let ((m, a), (b, n)) = (self.dim, rhs.dim);
        let (lhs_columns, rhs_rows) = (a, b);
        assert!(lhs_columns == rhs_rows);
        assert!(m.checked_mul(n).is_some());

        mat_mul_impl(self, rhs)
    }

    /// Perform the matrix multiplication of the rectangular array `self` and
    /// column vector `rhs`.
    ///
    /// The array shapes must agree in the way that
    /// if `self` is *M* × *N*, then `rhs` is *N*.
    ///
    /// Return a result array with shape *M*.
    ///
    /// **Panics** if shapes are incompatible.
    pub fn mat_mul_col(&self, rhs: &ArrayBase<S, Ix>) -> OwnedArray<A, Ix>
        where A: LinalgScalar,
    {
        let ((m, a), n) = (self.dim, rhs.dim);
        let (self_columns, other_rows) = (a, n);
        assert!(self_columns == other_rows);

        // Avoid initializing the memory in vec -- set it during iteration
        let mut res_elems = Vec::<A>::with_capacity(m as usize);
        unsafe {
            res_elems.set_len(m as usize);
        }
        for (i, rr) in enumerate(&mut res_elems) {
            unsafe {
                *rr = (0..a).fold(A::zero(),
                    move |s, k| s + *self.uget((i, k)) * *rhs.uget(k)
                );
            }
        }
        unsafe {
            ArrayBase::from_vec_dim_unchecked(m, res_elems)
        }
    }
}

#[cfg(not(feature="blas"))]
use self::mat_mul_general as mat_mul_impl;

#[cfg(feature="blas")]
fn mat_mul_impl<A, S>(lhs: &ArrayBase<S, (Ix, Ix)>, rhs: &ArrayBase<S, (Ix, Ix)>)
    -> OwnedArray<A, (Ix, Ix)>
    where A: LinalgScalar,
          S: Data<Elem=A>,
{
    // size cutoff for using BLAS
    let cut = GEMM_BLAS_CUTOFF;
    let ((mut m, a), (_, mut n)) = (lhs.dim, rhs.dim);
    if !(m > cut || n > cut || a > cut) ||
        !(same_type::<A, f32>() || same_type::<A, f64>()) {
        return mat_mul_general(lhs, rhs);
    }
    // Use `c` for c-order and `f` for an f-order matrix
    // We can handle c * c, f * f generally and
    // c * f and f * c if the `f` matrix is square.
    let mut lhs_ = lhs.view();
    let mut rhs_ = rhs.view();
    let lhs_s0 = lhs_.strides()[0];
    let rhs_s0 = rhs_.strides()[0];
    let both_f = lhs_s0 == 1 && rhs_s0 == 1;
    let mut lhs_trans = CblasNoTrans;
    let mut rhs_trans = CblasNoTrans;
    if both_f {
        // A^t B^t = C^t => B A = C
        lhs_ = lhs_.reversed_axes();
        rhs_ = rhs_.reversed_axes();
        swap(&mut lhs_, &mut rhs_);
        swap(&mut m, &mut n);
    } else if lhs_s0 == 1 && m == a {
        lhs_ = lhs_.reversed_axes();
        lhs_trans = CblasTrans;
    } else if rhs_s0 == 1 && a == n {
        rhs_ = rhs_.reversed_axes();
        rhs_trans = CblasTrans;
    }

    macro_rules! gemm {
        ($ty:ty, $gemm:ident) => {
        if blas_row_major_2d::<$ty, _>(&lhs_) && blas_row_major_2d::<$ty, _>(&rhs_) {
            let mut elems = Vec::<A>::with_capacity(m * n);
            let c;
            unsafe {
                elems.set_len(m * n);
                c = OwnedArray::from_vec_dim_unchecked((m, n), elems);
            }
            {
                let (m, k) = match lhs_trans {
                    CblasNoTrans => lhs_.dim(),
                    _ => {
                        let (rows, cols) = lhs_.dim();
                        (cols, rows)
                    }
                };
                let n = match rhs_trans {
                    CblasNoTrans => rhs_.dim().1,
                    _ => rhs_.dim().0,
                };
                // gemm is C ← αA^Op B^Op + βC
                // Where Op is notrans/trans/conjtrans
                unsafe {
                    blas_sys::c::$gemm(
                    CblasRowMajor,
                    lhs_trans,
                    rhs_trans,
                    m as c_int, // m, rows of Op(a)
                    n as c_int, // n, cols of Op(b)
                    k as c_int, // k, cols of Op(a)
                    1.0,                  // alpha
                    lhs_.ptr as *const _, // a
                    lhs_.strides()[0] as c_int, // lda
                    rhs_.ptr as *const _, // b
                    rhs_.strides()[0] as c_int, // ldb
                    0.0,                   // beta
                    c.ptr as *mut _,       // c
                    c.strides()[0] as c_int, // ldc
                );
                }
            }
            return if both_f {
                c.reversed_axes()
            } else {
                c
            };
        }
        }
    }
    gemm!(f32, cblas_sgemm);
    gemm!(f64, cblas_dgemm);
    return mat_mul_general(lhs, rhs);
}

fn mat_mul_general<A, S>(lhs: &ArrayBase<S, (Ix, Ix)>, rhs: &ArrayBase<S, (Ix, Ix)>)
    -> OwnedArray<A, (Ix, Ix)>
    where A: LinalgScalar,
          S: Data<Elem=A>,
{
    let ((m, a), (_, n)) = (lhs.dim, rhs.dim);

    let lhs_s0 = lhs.strides()[0];
    let rhs_s0 = rhs.strides()[0];
    let column_major = lhs_s0 == 1 && rhs_s0 == 1;

    // Avoid initializing the memory in vec -- set it during iteration
    // Panic safe because A: Copy
    let mut res_elems = Vec::<A>::with_capacity(m as usize * n as usize);
    unsafe {
        res_elems.set_len(m as usize * n as usize);
    }
    let mut i = 0;
    let mut j = 0;
    for rr in &mut res_elems {
        unsafe {
            *rr = (0..a).fold(A::zero(),
                move |s, k| s + *lhs.uget((i, k)) * *rhs.uget((k, j)));
        }
        if !column_major {
            j += 1;
            if j == n {
                j = 0;
                i += 1;
            }
        } else {
            i += 1;
            if i == m {
                i = 0;
                j += 1;
            }
        }
    }
    unsafe {
        if !column_major {
            ArrayBase::from_vec_dim_unchecked((m, n), res_elems)
        } else {
            ArrayBase::from_vec_dim_unchecked_f((m, n), res_elems)
        }
    }
}

#[cfg(feature="blas")]
#[inline(always)]
/// Return `true` if `A` and `B` are the same type
fn same_type<A: Any, B: Any>() -> bool {
    TypeId::of::<A>() == TypeId::of::<B>()
}

#[cfg(feature="blas")]
// Read pointer to type `A` as type `B`.
//
// **Panics** if `A` and `B` are not the same type
fn cast_as<A: Any + Copy, B: Any + Copy>(a: &A) -> B {
    assert!(same_type::<A, B>());
    unsafe {
        ::std::ptr::read(a as *const _ as *const B)
    }
}

#[cfg(feature="blas")]
fn blas_compat_1d<A, S>(a: &ArrayBase<S, Ix>) -> bool
    where S: Data,
          A: Any,
          S::Elem: Any,
{
    if !same_type::<A, S::Elem>() {
        return false;
    }
    if a.len() > c_int::max_value() as usize {
        return false;
    }
    let stride = a.strides()[0];
    if stride > c_int::max_value() as isize ||
        stride < c_int::min_value() as isize {
        return false;
    }
    true
}

#[cfg(feature="blas")]
fn blas_row_major_2d<A, S>(a: &ArrayBase<S, (Ix, Ix)>) -> bool
    where S: Data,
          A: Any,
          S::Elem: Any,
{
    if !same_type::<A, S::Elem>() {
        return false;
    }
    let s0 = a.strides()[0];
    let s1 = a.strides()[1];
    if s1 != 1 {
        return false;
    }
    if (s0 > c_int::max_value() as isize || s0 < c_int::min_value() as isize) ||
        (s1 > c_int::max_value() as isize || s1 < c_int::min_value() as isize)
    {
        return false;
    }
    let (m, n) = a.dim();
    if m > c_int::max_value() as usize ||
        n > c_int::max_value() as usize
    {
        return false;
    }
    true
}
<|endoftext|>


<commit_before>use parser::ParseError;
use std::collections::HashSet;
use text_range::TextRange;

pub struct InputError {
    /// The error message
    pub msg: String,
    /// The columns in which the error occurred
    pub pos: TextRange,
}

impl InputError {
    pub fn from_parser_error(err: ParseError) -> InputError {
        InputError {
            msg: generate_expected_string(&err.expected),
            pos: TextRange {
                start: err.offset,
                end: err.column,
            },
        }
    }
}

fn generate_expected_string(expected: &HashSet<&str>) -> String {
    let mut expected = expected.iter().map(|s| *s).collect::<Vec<&str>>();

    expected.sort();

    format!("expected {}", or_list(&expected))
}

#[test]
fn test_generate_expected_string() {
    let mut expected = HashSet::new();
    expected.insert("abc");
    expected.insert("number");
    assert_eq!(generate_expected_string(&expected), "expected abc or number");
}

#[test]
fn test_generate_expected_string_with_special_characters() {
    let mut expected = HashSet::new();
    expected.insert("a");
    expected.insert("(");
    expected.insert("0");
    expected.insert("number");
    assert_eq!(generate_expected_string(&expected), "expected (, 0, a or number");
}

fn or_list(items: &[&str]) -> String {
    let len = items.len();
    match len {
        0 => String::new(),
        1 => String::from(items[0]),
        _ => {
            let last = len - 1;
            format!("{} or {}", items[0..last].join(", "), items[last])
        }
    }
}

#[test]
fn test_or_list_with_one_entry() {
    assert_eq!(or_list(&vec!["foo"]), String::from("foo"));
}

#[test]
fn test_or_list_with_two_entries() {
    assert_eq!(or_list(&vec!["foo", "bar"]), String::from("foo or bar"));
}

#[test]
fn test_or_list_with_three_entries() {
    assert_eq!(
        or_list(&vec!["foo", "bar", "baz"]),
        String::from("foo, bar or baz")
    );
}

#[test]
fn test_or_list_with_five_entries() {
    assert_eq!(
        or_list(&vec!["a", "b", "c", "d", "e"]),
        String::from("a, b, c, d or e")
    );
}
<commit_msg>Fix format<commit_after>use parser::ParseError;
use std::collections::HashSet;
use text_range::TextRange;

pub struct InputError {
    /// The error message
    pub msg: String,
    /// The columns in which the error occurred
    pub pos: TextRange,
}

impl InputError {
    pub fn from_parser_error(err: ParseError) -> InputError {
        InputError {
            msg: generate_expected_string(&err.expected),
            pos: TextRange {
                start: err.offset,
                end: err.column,
            },
        }
    }
}

fn generate_expected_string(expected: &HashSet<&str>) -> String {
    let mut expected = expected.iter().map(|s| *s).collect::<Vec<&str>>();

    expected.sort();

    format!("expected {}", or_list(&expected))
}

#[test]
fn test_generate_expected_string() {
    let mut expected = HashSet::new();
    expected.insert("abc");
    expected.insert("number");
    assert_eq!(
        generate_expected_string(&expected),
        "expected abc or number"
    );
}

#[test]
fn test_generate_expected_string_with_special_characters() {
    let mut expected = HashSet::new();
    expected.insert("a");
    expected.insert("(");
    expected.insert("0");
    expected.insert("number");
    assert_eq!(
        generate_expected_string(&expected),
        "expected (, 0, a or number"
    );
}

fn or_list(items: &[&str]) -> String {
    let len = items.len();
    match len {
        0 => String::new(),
        1 => String::from(items[0]),
        _ => {
            let last = len - 1;
            format!("{} or {}", items[0..last].join(", "), items[last])
        }
    }
}

#[test]
fn test_or_list_with_one_entry() {
    assert_eq!(or_list(&vec!["foo"]), String::from("foo"));
}

#[test]
fn test_or_list_with_two_entries() {
    assert_eq!(or_list(&vec!["foo", "bar"]), String::from("foo or bar"));
}

#[test]
fn test_or_list_with_three_entries() {
    assert_eq!(
        or_list(&vec!["foo", "bar", "baz"]),
        String::from("foo, bar or baz")
    );
}

#[test]
fn test_or_list_with_five_entries() {
    assert_eq!(
        or_list(&vec!["a", "b", "c", "d", "e"]),
        String::from("a, b, c, d or e")
    );
}
<|endoftext|>


<commit_before>use chan;
use chan::{Sender, Receiver};
use std;
use std::borrow::Cow;

use datatype::{AccessToken, Auth, ClientId, ClientSecret, Command, Config,
               Error, Event, Package, UpdateRequestId};
use gateway::Interpret;
use http::{AuthClient, Client};
use oauth2::authenticate;
use package_manager::PackageManager;
use rvi::Services;
use sota::Sota;

/// An `Interpreter` loops over any incoming values, on receipt of which it
/// delegates to the `interpret` function which will respond with output values.
pub trait Interpreter<I, O> {
    fn interpret(&mut self, input: I, otx: &Sender<O>);

    fn run(&mut self, irx: Receiver<I>, otx: Sender<O>) {
        loop {
            self.interpret(irx.recv().expect("interpreter sender closed"), &otx);
        }
    }
}

/// The `EventInterpreter` listens for `Event`s and optionally responds with
/// `Command`s that may be sent to the `CommandInterpreter`.
pub struct EventInterpreter {
    pub package_manager: PackageManager
}

impl Interpreter<Event, Command> for EventInterpreter {
    fn interpret(&mut self, event: Event, ctx: &Sender<Command>) {
        info!("Event received: {}", event);
        match event {
            Event::NotAuthenticated => {
                info!("Trying to authenticate again...");
                ctx.send(Command::Authenticate(None));
            }

            Event::NewUpdatesReceived(ids) => {
                ctx.send(Command::StartDownload(ids));
            }

            Event::DownloadComplete(dl) => {
                if self.package_manager != PackageManager::Off {
                    ctx.send(Command::StartInstall(dl));
                }
            }

            Event::InstallComplete(report) => {
                ctx.send(Command::SendUpdateReport(report));
            }

            _ => ()
        }
    }
}

/// The `CommandInterpreter` wraps each incoming `Command` inside an `Interpret`
/// type with no response channel for sending to the `GlobalInterpreter`.
pub struct CommandInterpreter;

impl Interpreter<Command, Interpret> for CommandInterpreter {
    fn interpret(&mut self, cmd: Command, itx: &Sender<Interpret>) {
        info!("Command received: {}", cmd);
        itx.send(Interpret { command: cmd, response_tx: None });
    }
}

/// The `GlobalInterpreter` interprets the `Command` inside incoming `Interpret`
/// messages, broadcasting `Event`s globally and (optionally) sending the final
/// outcome `Event` to the `Interpret` response channel.
pub struct GlobalInterpreter<'t> {
    pub config:      Config,
    pub token:       Option<Cow<'t, AccessToken>>,
    pub http_client: Box<Client>,
    pub rvi:         Option<Services>,
    pub loopback_tx: Sender<Interpret>,
}

impl<'t> Interpreter<Interpret, Event> for GlobalInterpreter<'t> {
    fn interpret(&mut self, interpret: Interpret, etx: &Sender<Event>) {
        info!("Interpreter started: {}", interpret.command);

        let (multi_tx, multi_rx) = chan::async::<Event>();
        let outcome = match (self.token.as_ref(), self.config.auth.is_none()) {
            (Some(_), _) | (_, true) => self.authenticated(interpret.command, multi_tx),
            _                        => self.unauthenticated(interpret.command, multi_tx)
        };

        let mut response_ev: Option<Event> = None;
        match outcome {
            Ok(_) => {
                for ev in multi_rx {
                    etx.send(ev.clone());
                    response_ev = Some(ev);
                }
                info!("Interpreter finished.");
            }

            Err(Error::Authorization(_)) => {
                let ev = Event::NotAuthenticated;
                etx.send(ev.clone());
                response_ev = Some(ev);
                error!("Interpreter authentication failed");
            }

            Err(err) => {
                let ev = Event::Error(format!("{}", err));
                etx.send(ev.clone());
                response_ev = Some(ev);
                error!("Interpreter failed: {}", err);
            }
        }

        let ev = response_ev.expect("no response event to send back");
        interpret.response_tx.map(|tx| tx.lock().unwrap().send(ev));
    }
}

impl<'t> GlobalInterpreter<'t> {
    fn authenticated(&self, cmd: Command, etx: Sender<Event>) -> Result<(), Error> {
        let mut sota = Sota::new(&self.config, self.http_client.as_ref());

        // always send at least one Event response
        match cmd {
            Command::Authenticate(_) => etx.send(Event::Authenticated),

            Command::GetNewUpdates => {
                let mut updates = try!(sota.get_pending_updates());
                if updates.is_empty() {
                    etx.send(Event::NoNewUpdates);
                } else {
                    updates.sort_by_key(|u| u.installPos);
                    let ids = updates.iter().map(|u| u.requestId.clone()).collect::<Vec<UpdateRequestId>>();
                    etx.send(Event::NewUpdatesReceived(ids))
                }
            }

            Command::ListInstalledPackages => {
                let mut packages: Vec<Package> = Vec::new();
                if self.config.device.package_manager != PackageManager::Off {
                    packages = try!(self.config.device.package_manager.installed_packages());
                }
                etx.send(Event::FoundInstalledPackages(packages));
            }

            Command::RefreshSystemInfo(post) => {
                let info = try!(self.config.device.system_info.report());
                etx.send(Event::FoundSystemInfo(info.clone()));
                if post {
                    let _ = sota.send_system_info(&info)
                        .map_err(|err| etx.send(Event::Error(format!("{}", err))));
                }
            }

            Command::SendInstalledPackages(packages) => {
                let _ = sota.send_installed_packages(&packages)
                    .map_err(|err| error!("couldn't send installed packages: {}", err));
                etx.send(Event::InstalledPackagesSent);
            }

            Command::SendInstalledSoftware(sw) => {
                self.rvi.as_ref().map(|rvi| rvi.remote.lock().unwrap().send_installed_software(sw));
            }

            Command::SendUpdateReport(report) => {
                let _ = sota.send_update_report(&report)
                    .map_err(|err| error!("couldn't send update report: {}", err));
                self.rvi.as_ref().map(|rvi| rvi.remote.lock().unwrap().send_update_report(report));
                etx.send(Event::UpdateReportSent);
            }

            Command::StartDownload(ref ids) => {
                for id in ids {
                    etx.send(Event::DownloadingUpdate(id.clone()));
                    self.rvi.as_ref().map(|rvi| rvi.remote.lock().unwrap().send_download_started(id.clone()));
                    let _ = sota.download_update(id.clone())
                        .map(|dl| etx.send(Event::DownloadComplete(dl)))
                        .map_err(|err| etx.send(Event::DownloadFailed(id.clone(), format!("{}", err))));
                }
            }

            Command::StartInstall(dl) => {
                let _ = sota.install_update(dl)
                    .map(|report| etx.send(Event::InstallComplete(report)))
                    .map_err(|report| etx.send(Event::InstallFailed(report)));
            }

            Command::Shutdown => std::process::exit(0),
        }

        Ok(())
    }

    fn unauthenticated(&mut self, cmd: Command, etx: Sender<Event>) -> Result<(), Error> {
        match cmd {
            Command::Authenticate(_) => {
                let config = self.config.auth.clone().expect("trying to authenticate without auth config");
                self.set_client(Auth::Credentials(ClientId(config.client_id),
                                                  ClientSecret(config.client_secret)));
                let server = config.server.join("/token").expect("couldn't build authentication url");
                let token  = try!(authenticate(server, self.http_client.as_ref()));
                self.set_client(Auth::Token(token.clone()));
                self.token = Some(token.into());
                etx.send(Event::Authenticated);
            }

            Command::GetNewUpdates            |
            Command::ListInstalledPackages    |
            Command::RefreshSystemInfo(_)     |
            Command::SendInstalledPackages(_) |
            Command::SendInstalledSoftware(_) |
            Command::SendUpdateReport(_)      |
            Command::StartDownload(_)         |
            Command::StartInstall(_)          => etx.send(Event::NotAuthenticated),

            Command::Shutdown => std::process::exit(0),
        }

        Ok(())
    }

    fn set_client(&mut self, auth: Auth) {
        if !self.http_client.is_testing() {
            self.http_client = Box::new(AuthClient::from(auth));
        }
    }
}

#[cfg(test)]
mod tests {
    use chan;
    use chan::{Sender, Receiver};
    use std::thread;

    use super::*;
    use datatype::{AccessToken, Command, Config, DownloadComplete, Event,
                   UpdateReport, UpdateResultCode};
    use gateway::Interpret;
    use http::test_client::TestClient;
    use package_manager::PackageManager;
    use package_manager::tpm::assert_rx;

    fn new_interpreter(replies: Vec<String>, pkg_mgr: PackageManager) -> (Sender<Command>, Receiver<Event>) {
        let (etx, erx) = chan::sync::<Event>(0);
        let (ctx, crx) = chan::sync::<Command>(0);
        let (itx, _)   = chan::sync::<Interpret>(0);

        thread::spawn(move || {
            let mut gi = GlobalInterpreter {
                config:      Config::default(),
                token:       Some(AccessToken::default().into()),
                http_client: Box::new(TestClient::from(replies)),
                rvi:         None,
                loopback_tx: itx,
            };
            gi.config.device.package_manager = pkg_mgr;

            loop {
                match crx.recv() {
                    Some(cmd) => gi.interpret(Interpret { command: cmd, response_tx: None }, &etx),
                    None      => break
                }
            }
        });

        (ctx, erx)
    }

    #[test]
    fn already_authenticated() {
        let replies    = Vec::new();
        let pkg_mgr    = PackageManager::new_tpm(true);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::Authenticate(None));
        assert_rx(erx, &[Event::Authenticated]);
    }

    #[test]
    fn download_updates() {
        let replies    = vec!["[]".to_string(); 10];
        let pkg_mgr    = PackageManager::new_tpm(true);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::StartDownload(vec!["1".to_string(), "2".to_string()]));
        assert_rx(erx, &[
            Event::DownloadingUpdate("1".to_string()),
            Event::DownloadComplete(DownloadComplete {
                update_id:    "1".to_string(),
                update_image: "/tmp/1".to_string(),
                signature:    "".to_string()
            }),
            Event::DownloadingUpdate("2".to_string()),
            Event::DownloadComplete(DownloadComplete {
                update_id:    "2".to_string(),
                update_image: "/tmp/2".to_string(),
                signature:    "".to_string()
            }),
        ]);
    }

    #[test]
    fn install_update() {
        let replies    = vec!["[]".to_string(); 10];
        let pkg_mgr    = PackageManager::new_tpm(true);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::StartInstall(DownloadComplete {
            update_id:    "1".to_string(),
            update_image: "/tmp/1".to_string(),
            signature:    "".to_string()
        }));
        assert_rx(erx, &[
            Event::InstallComplete(
                UpdateReport::single("1".to_string(), UpdateResultCode::OK, "".to_string())
            )
        ]);
    }

    #[test]
    fn failed_installation() {
        let replies    = vec!["[]".to_string(); 10];
        let pkg_mgr    = PackageManager::new_tpm(false);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::StartInstall(DownloadComplete {
            update_id:    "1".to_string(),
            update_image: "/tmp/1".to_string(),
            signature:    "".to_string()
        }));
        assert_rx(erx, &[
            Event::InstallFailed(
                UpdateReport::single("1".to_string(), UpdateResultCode::INSTALL_FAILED, "failed".to_string())
            )
        ]);
    }
}
<commit_msg>Use RVI only if enabled in interpreter<commit_after>use chan;
use chan::{Sender, Receiver};
use std;
use std::borrow::Cow;

use datatype::{AccessToken, Auth, ClientId, ClientSecret, Command, Config,
               Error, Event, Package, UpdateRequestId};
use gateway::Interpret;
use http::{AuthClient, Client};
use oauth2::authenticate;
use package_manager::PackageManager;
use rvi::Services;
use sota::Sota;

/// An `Interpreter` loops over any incoming values, on receipt of which it
/// delegates to the `interpret` function which will respond with output values.
pub trait Interpreter<I, O> {
    fn interpret(&mut self, input: I, otx: &Sender<O>);

    fn run(&mut self, irx: Receiver<I>, otx: Sender<O>) {
        loop {
            self.interpret(irx.recv().expect("interpreter sender closed"), &otx);
        }
    }
}

/// The `EventInterpreter` listens for `Event`s and optionally responds with
/// `Command`s that may be sent to the `CommandInterpreter`.
pub struct EventInterpreter {
    pub package_manager: PackageManager
}

impl Interpreter<Event, Command> for EventInterpreter {
    fn interpret(&mut self, event: Event, ctx: &Sender<Command>) {
        info!("Event received: {}", event);
        match event {
            Event::NotAuthenticated => {
                info!("Trying to authenticate again...");
                ctx.send(Command::Authenticate(None));
            }

            Event::NewUpdatesReceived(ids) => {
                ctx.send(Command::StartDownload(ids));
            }

            Event::DownloadComplete(dl) => {
                if self.package_manager != PackageManager::Off {
                    ctx.send(Command::StartInstall(dl));
                }
            }

            Event::InstallComplete(report) => {
                ctx.send(Command::SendUpdateReport(report));
            }

            _ => ()
        }
    }
}

/// The `CommandInterpreter` wraps each incoming `Command` inside an `Interpret`
/// type with no response channel for sending to the `GlobalInterpreter`.
pub struct CommandInterpreter;

impl Interpreter<Command, Interpret> for CommandInterpreter {
    fn interpret(&mut self, cmd: Command, itx: &Sender<Interpret>) {
        info!("Command received: {}", cmd);
        itx.send(Interpret { command: cmd, response_tx: None });
    }
}

/// The `GlobalInterpreter` interprets the `Command` inside incoming `Interpret`
/// messages, broadcasting `Event`s globally and (optionally) sending the final
/// outcome `Event` to the `Interpret` response channel.
pub struct GlobalInterpreter<'t> {
    pub config:      Config,
    pub token:       Option<Cow<'t, AccessToken>>,
    pub http_client: Box<Client>,
    pub rvi:         Option<Services>,
    pub loopback_tx: Sender<Interpret>,
}

impl<'t> Interpreter<Interpret, Event> for GlobalInterpreter<'t> {
    fn interpret(&mut self, interpret: Interpret, etx: &Sender<Event>) {
        info!("Interpreter started: {}", interpret.command);

        let (multi_tx, multi_rx) = chan::async::<Event>();
        let outcome = match (self.token.as_ref(), self.config.auth.is_none()) {
            (Some(_), _) | (_, true) => self.authenticated(interpret.command, multi_tx),
            _                        => self.unauthenticated(interpret.command, multi_tx)
        };

        let mut response_ev: Option<Event> = None;
        match outcome {
            Ok(_) => {
                for ev in multi_rx {
                    etx.send(ev.clone());
                    response_ev = Some(ev);
                }
                info!("Interpreter finished.");
            }

            Err(Error::Authorization(_)) => {
                let ev = Event::NotAuthenticated;
                etx.send(ev.clone());
                response_ev = Some(ev);
                error!("Interpreter authentication failed");
            }

            Err(err) => {
                let ev = Event::Error(format!("{}", err));
                etx.send(ev.clone());
                response_ev = Some(ev);
                error!("Interpreter failed: {}", err);
            }
        }

        let ev = response_ev.expect("no response event to send back");
        interpret.response_tx.map(|tx| tx.lock().unwrap().send(ev));
    }
}

impl<'t> GlobalInterpreter<'t> {
    fn authenticated(&self, cmd: Command, etx: Sender<Event>) -> Result<(), Error> {
        let mut sota = Sota::new(&self.config, self.http_client.as_ref());

        // always send at least one Event response
        match cmd {
            Command::Authenticate(_) => etx.send(Event::Authenticated),

            Command::GetNewUpdates => {
                let mut updates = try!(sota.get_pending_updates());
                if updates.is_empty() {
                    etx.send(Event::NoNewUpdates);
                } else {
                    updates.sort_by_key(|u| u.installPos);
                    let ids = updates.iter().map(|u| u.requestId.clone()).collect::<Vec<UpdateRequestId>>();
                    etx.send(Event::NewUpdatesReceived(ids))
                }
            }

            Command::ListInstalledPackages => {
                let mut packages: Vec<Package> = Vec::new();
                if self.config.device.package_manager != PackageManager::Off {
                    packages = try!(self.config.device.package_manager.installed_packages());
                }
                etx.send(Event::FoundInstalledPackages(packages));
            }

            Command::RefreshSystemInfo(post) => {
                let info = try!(self.config.device.system_info.report());
                etx.send(Event::FoundSystemInfo(info.clone()));
                if post {
                    let _ = sota.send_system_info(&info)
                        .map_err(|err| etx.send(Event::Error(format!("{}", err))));
                }
            }

            Command::SendInstalledPackages(packages) => {
                let _ = sota.send_installed_packages(&packages)
                    .map_err(|err| error!("couldn't send installed packages: {}", err));
                etx.send(Event::InstalledPackagesSent);
            }

            Command::SendInstalledSoftware(sw) => {
                if let Some(ref rvi) = self.rvi {
                    let _ = rvi.remote.lock().unwrap().send_installed_software(sw);
                }
            }

            Command::SendUpdateReport(report) => {
                if let Some(ref rvi) = self.rvi {
                    let _ = rvi.remote.lock().unwrap().send_update_report(report);
                } else {
                    let _ = sota.send_update_report(&report)
                        .map_err(|err| error!("couldn't send update report: {}", err));
                }
                etx.send(Event::UpdateReportSent);
            }

            Command::StartDownload(ref ids) => {
                for id in ids {
                    etx.send(Event::DownloadingUpdate(id.clone()));

                    if let Some(ref rvi) = self.rvi {
                        let _ = rvi.remote.lock().unwrap().send_download_started(id.clone());
                    } else {
                        let _ = sota.download_update(id.clone())
                            .map(|dl| etx.send(Event::DownloadComplete(dl)))
                            .map_err(|err| etx.send(Event::DownloadFailed(id.clone(), format!("{}", err))));
                    }
                }
            }

            Command::StartInstall(dl) => {
                let _ = sota.install_update(dl)
                    .map(|report| etx.send(Event::InstallComplete(report)))
                    .map_err(|report| etx.send(Event::InstallFailed(report)));
            }

            Command::Shutdown => std::process::exit(0),
        }

        Ok(())
    }

    fn unauthenticated(&mut self, cmd: Command, etx: Sender<Event>) -> Result<(), Error> {
        match cmd {
            Command::Authenticate(_) => {
                let config = self.config.auth.clone().expect("trying to authenticate without auth config");
                self.set_client(Auth::Credentials(ClientId(config.client_id),
                                                  ClientSecret(config.client_secret)));
                let server = config.server.join("/token").expect("couldn't build authentication url");
                let token  = try!(authenticate(server, self.http_client.as_ref()));
                self.set_client(Auth::Token(token.clone()));
                self.token = Some(token.into());
                etx.send(Event::Authenticated);
            }

            Command::GetNewUpdates            |
            Command::ListInstalledPackages    |
            Command::RefreshSystemInfo(_)     |
            Command::SendInstalledPackages(_) |
            Command::SendInstalledSoftware(_) |
            Command::SendUpdateReport(_)      |
            Command::StartDownload(_)         |
            Command::StartInstall(_)          => etx.send(Event::NotAuthenticated),

            Command::Shutdown => std::process::exit(0),
        }

        Ok(())
    }

    fn set_client(&mut self, auth: Auth) {
        if !self.http_client.is_testing() {
            self.http_client = Box::new(AuthClient::from(auth));
        }
    }
}

#[cfg(test)]
mod tests {
    use chan;
    use chan::{Sender, Receiver};
    use std::thread;

    use super::*;
    use datatype::{AccessToken, Command, Config, DownloadComplete, Event,
                   UpdateReport, UpdateResultCode};
    use gateway::Interpret;
    use http::test_client::TestClient;
    use package_manager::PackageManager;
    use package_manager::tpm::assert_rx;

    fn new_interpreter(replies: Vec<String>, pkg_mgr: PackageManager) -> (Sender<Command>, Receiver<Event>) {
        let (etx, erx) = chan::sync::<Event>(0);
        let (ctx, crx) = chan::sync::<Command>(0);
        let (itx, _)   = chan::sync::<Interpret>(0);

        thread::spawn(move || {
            let mut gi = GlobalInterpreter {
                config:      Config::default(),
                token:       Some(AccessToken::default().into()),
                http_client: Box::new(TestClient::from(replies)),
                rvi:         None,
                loopback_tx: itx,
            };
            gi.config.device.package_manager = pkg_mgr;

            loop {
                match crx.recv() {
                    Some(cmd) => gi.interpret(Interpret { command: cmd, response_tx: None }, &etx),
                    None      => break
                }
            }
        });

        (ctx, erx)
    }

    #[test]
    fn already_authenticated() {
        let replies    = Vec::new();
        let pkg_mgr    = PackageManager::new_tpm(true);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::Authenticate(None));
        assert_rx(erx, &[Event::Authenticated]);
    }

    #[test]
    fn download_updates() {
        let replies    = vec!["[]".to_string(); 10];
        let pkg_mgr    = PackageManager::new_tpm(true);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::StartDownload(vec!["1".to_string(), "2".to_string()]));
        assert_rx(erx, &[
            Event::DownloadingUpdate("1".to_string()),
            Event::DownloadComplete(DownloadComplete {
                update_id:    "1".to_string(),
                update_image: "/tmp/1".to_string(),
                signature:    "".to_string()
            }),
            Event::DownloadingUpdate("2".to_string()),
            Event::DownloadComplete(DownloadComplete {
                update_id:    "2".to_string(),
                update_image: "/tmp/2".to_string(),
                signature:    "".to_string()
            }),
        ]);
    }

    #[test]
    fn install_update() {
        let replies    = vec!["[]".to_string(); 10];
        let pkg_mgr    = PackageManager::new_tpm(true);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::StartInstall(DownloadComplete {
            update_id:    "1".to_string(),
            update_image: "/tmp/1".to_string(),
            signature:    "".to_string()
        }));
        assert_rx(erx, &[
            Event::InstallComplete(
                UpdateReport::single("1".to_string(), UpdateResultCode::OK, "".to_string())
            )
        ]);
    }

    #[test]
    fn failed_installation() {
        let replies    = vec!["[]".to_string(); 10];
        let pkg_mgr    = PackageManager::new_tpm(false);
        let (ctx, erx) = new_interpreter(replies, pkg_mgr);

        ctx.send(Command::StartInstall(DownloadComplete {
            update_id:    "1".to_string(),
            update_image: "/tmp/1".to_string(),
            signature:    "".to_string()
        }));
        assert_rx(erx, &[
            Event::InstallFailed(
                UpdateReport::single("1".to_string(), UpdateResultCode::INSTALL_FAILED, "failed".to_string())
            )
        ]);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{HashValue, Transaction, TransactionOnChainData, U64};
use poem_openapi::Object;
use serde::{Deserialize, Serialize};

/// A Block with or without transactions
///
/// This contains the information about a transactions along with
/// associated transactions if requested
#[derive(Debug, Clone, Serialize, Deserialize, Object)]
pub struct Block {
    pub block_height: U64,
    pub block_hash: HashValue,
    pub block_timestamp: U64,
    pub first_version: U64,
    pub last_version: U64,
    /// The transactions in the block in sequential order
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transactions: Option<Vec<Transaction>>,
}

/// A Block with or without transactions for encoding in BCS
///
/// This contains the information about a transactions along with
/// associated transactions if requested
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BcsBlock {
    /// The block height (number of the block from 0)
    pub block_height: u64,
    pub block_hash: aptos_crypto::HashValue,
    /// The block timestamp in Unix epoch microseconds
    pub block_timestamp: u64,
    /// The first ledger version of the block inclusive
    pub first_version: u64,
    /// The last ledger version of the block inclusive
    pub last_version: u64,
    /// The transactions in the block in sequential order
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transactions: Option<Vec<TransactionOnChainData>>,
}
<commit_msg>[api] Fix BCS output of blocks<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::{HashValue, Transaction, TransactionOnChainData, U64};
use poem_openapi::Object;
use serde::{Deserialize, Serialize};

/// A Block with or without transactions
///
/// This contains the information about a transactions along with
/// associated transactions if requested
#[derive(Debug, Clone, Serialize, Deserialize, Object)]
pub struct Block {
    pub block_height: U64,
    pub block_hash: HashValue,
    pub block_timestamp: U64,
    pub first_version: U64,
    pub last_version: U64,
    /// The transactions in the block in sequential order
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transactions: Option<Vec<Transaction>>,
}

/// A Block with or without transactions for encoding in BCS
///
/// This contains the information about a transactions along with
/// associated transactions if requested
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BcsBlock {
    /// The block height (number of the block from 0)
    pub block_height: u64,
    pub block_hash: aptos_crypto::HashValue,
    /// The block timestamp in Unix epoch microseconds
    pub block_timestamp: u64,
    /// The first ledger version of the block inclusive
    pub first_version: u64,
    /// The last ledger version of the block inclusive
    pub last_version: u64,
    /// The transactions in the block in sequential order
    pub transactions: Option<Vec<TransactionOnChainData>>,
}
<|endoftext|>