<commit_before>//! FIXME: write short doc here

use std::{
    convert::From,
    fmt::{self, Display},
};

use hir::{Docs, Documentation, HasSource, HirDisplay};
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
use stdx::SepBy;

use crate::display::{generic_parameters, where_predicates};

#[derive(Debug)]
pub enum CallableKind {
    Function,
    StructConstructor,
    VariantConstructor,
    Macro,
}

/// Contains information about a function signature
#[derive(Debug)]
pub struct FunctionSignature {
    pub kind: CallableKind,
    /// Optional visibility
    pub visibility: Option<String>,
    /// Name of the function
    pub name: Option<String>,
    /// Documentation for the function
    pub doc: Option<Documentation>,
    /// Generic parameters
    pub generic_parameters: Vec<String>,
    /// Parameters of the function
    pub parameters: Vec<String>,
    /// Parameter names of the function
    pub parameter_names: Vec<String>,
    /// Parameter types of the function
    pub parameter_types: Vec<String>,
    /// Optional return type
    pub ret_type: Option<String>,
    /// Where predicates
    pub where_predicates: Vec<String>,
    /// Self param presence
    pub has_self_param: bool,
}

impl FunctionSignature {
    pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
        self.doc = doc;
        self
    }

    pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
        let doc = function.docs(db);
        let ast_node = function.source(db).value;
        FunctionSignature::from(&ast_node).with_doc_opt(doc)
    }

    pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
        let node: ast::StructDef = st.source(db).value;
        if let ast::StructKind::Record(_) = node.kind() {
            return None;
        };

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in st.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));

            if let Some(param_type) = raw_param.split(':').nth(1) {
                parameter_types.push(param_type[1..].to_string());
            } else {
                // useful when you have tuple struct
                parameter_types.push(raw_param.clone());
            }
            params.push(raw_param);
        }

        Some(
            FunctionSignature {
                kind: CallableKind::StructConstructor,
                visibility: node.visibility().map(|n| n.syntax().text().to_string()),
                name: node.name().map(|n| n.text().to_string()),
                ret_type: node.name().map(|n| n.text().to_string()),
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: generic_parameters(&node),
                where_predicates: where_predicates(&node),
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(st.docs(db)),
        )
    }

    pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
        let node: ast::EnumVariant = variant.source(db).value;
        match node.kind() {
            ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
            _ => (),
        };

        let parent_name = variant.parent_enum(db).name(db).to_string();

        let name = format!("{}::{}", parent_name, variant.name(db));

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in variant.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));
            if let Some(param_type) = raw_param.split(':').nth(1) {
                parameter_types.push(param_type[1..].to_string());
            } else {
                // The unwrap_or_else is useful when you have tuple
                parameter_types.push(raw_param);
            }
            let name = field.name(db);

            params.push(format!("{}: {}", name, ty.display(db)));
        }

        Some(
            FunctionSignature {
                kind: CallableKind::VariantConstructor,
                visibility: None,
                name: Some(name),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(variant.docs(db)),
        )
    }

    pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
        let node: ast::MacroCall = macro_def.source(db).value;

        let params = vec![];

        Some(
            FunctionSignature {
                kind: CallableKind::Macro,
                visibility: None,
                name: node.name().map(|n| n.text().to_string()),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types: vec![],
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(macro_def.docs(db)),
        )
    }
}

impl From<&'_ ast::FnDef> for FunctionSignature {
    fn from(node: &ast::FnDef) -> FunctionSignature {
        fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
            let mut res = vec![];
            let mut res_types = vec![];
            let mut has_self_param = false;
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    has_self_param = true;
                    let raw_param = self_param.syntax().text().to_string();

                    res_types.push(
                        raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(),
                    );
                    res.push(raw_param);
                }

                res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
                res_types.extend(param_list.params().map(|param| {
                    param.syntax().text().to_string().split(':').nth(1).unwrap()[1..].to_string()
                }));
            }
            (has_self_param, res, res_types)
        }

        fn param_name_list(node: &ast::FnDef) -> Vec<String> {
            let mut res = vec![];
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    res.push(self_param.syntax().text().to_string())
                }

                res.extend(
                    param_list
                        .params()
                        .map(|param| {
                            Some(
                                param
                                    .pat()?
                                    .syntax()
                                    .descendants()
                                    .find_map(ast::Name::cast)?
                                    .text()
                                    .to_string(),
                            )
                        })
                        .map(|param| param.unwrap_or_default()),
                );
            }
            res
        }

        let (has_self_param, parameters, parameter_types) = param_list(node);

        FunctionSignature {
            kind: CallableKind::Function,
            visibility: node.visibility().map(|n| n.syntax().text().to_string()),
            name: node.name().map(|n| n.text().to_string()),
            ret_type: node
                .ret_type()
                .and_then(|r| r.type_ref())
                .map(|n| n.syntax().text().to_string()),
            parameters,
            parameter_names: param_name_list(node),
            parameter_types,
            generic_parameters: generic_parameters(node),
            where_predicates: where_predicates(node),
            // docs are processed separately
            doc: None,
            has_self_param,
        }
    }
}

impl Display for FunctionSignature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(t) = &self.visibility {
            write!(f, "{} ", t)?;
        }

        if let Some(name) = &self.name {
            match self.kind {
                CallableKind::Function => write!(f, "fn {}", name)?,
                CallableKind::StructConstructor => write!(f, "struct {}", name)?,
                CallableKind::VariantConstructor => write!(f, "{}", name)?,
                CallableKind::Macro => write!(f, "{}!", name)?,
            }
        }

        if !self.generic_parameters.is_empty() {
            write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
        }

        write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;

        if let Some(t) = &self.ret_type {
            write!(f, " -> {}", t)?;
        }

        if !self.where_predicates.is_empty() {
            write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n      "))?;
        }

        Ok(())
    }
}
<commit_msg>Include function qualifiers in signature<commit_after>//! FIXME: write short doc here

use std::{
    convert::From,
    fmt::{self, Display},
};

use hir::{Docs, Documentation, HasSource, HirDisplay};
use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
use stdx::SepBy;

use crate::display::{generic_parameters, where_predicates};

#[derive(Debug)]
pub enum CallableKind {
    Function,
    StructConstructor,
    VariantConstructor,
    Macro,
}

/// Contains information about a function signature
#[derive(Debug)]
pub struct FunctionSignature {
    pub kind: CallableKind,
    /// Optional visibility
    pub visibility: Option<String>,
    /// Qualifiers like `async`, `unsafe`, ...
    pub qualifier: FunctionQualifier,
    /// Name of the function
    pub name: Option<String>,
    /// Documentation for the function
    pub doc: Option<Documentation>,
    /// Generic parameters
    pub generic_parameters: Vec<String>,
    /// Parameters of the function
    pub parameters: Vec<String>,
    /// Parameter names of the function
    pub parameter_names: Vec<String>,
    /// Parameter types of the function
    pub parameter_types: Vec<String>,
    /// Optional return type
    pub ret_type: Option<String>,
    /// Where predicates
    pub where_predicates: Vec<String>,
    /// Self param presence
    pub has_self_param: bool,
}

#[derive(Debug, Default)]
pub struct FunctionQualifier {
    // `async` and `const` are mutually exclusive. Do we need to enforcing it here?
    pub is_async: bool,
    pub is_const: bool,
    pub is_unsafe: bool,
    /// The string `extern ".."`
    pub extern_abi: Option<String>,
}

impl FunctionSignature {
    pub(crate) fn with_doc_opt(mut self, doc: Option<Documentation>) -> Self {
        self.doc = doc;
        self
    }

    pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
        let doc = function.docs(db);
        let ast_node = function.source(db).value;
        FunctionSignature::from(&ast_node).with_doc_opt(doc)
    }

    pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
        let node: ast::StructDef = st.source(db).value;
        if let ast::StructKind::Record(_) = node.kind() {
            return None;
        };

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in st.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));

            if let Some(param_type) = raw_param.split(':').nth(1) {
                parameter_types.push(param_type[1..].to_string());
            } else {
                // useful when you have tuple struct
                parameter_types.push(raw_param.clone());
            }
            params.push(raw_param);
        }

        Some(
            FunctionSignature {
                kind: CallableKind::StructConstructor,
                visibility: node.visibility().map(|n| n.syntax().text().to_string()),
                // Do we need `const`?
                qualifier: Default::default(),
                name: node.name().map(|n| n.text().to_string()),
                ret_type: node.name().map(|n| n.text().to_string()),
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: generic_parameters(&node),
                where_predicates: where_predicates(&node),
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(st.docs(db)),
        )
    }

    pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
        let node: ast::EnumVariant = variant.source(db).value;
        match node.kind() {
            ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
            _ => (),
        };

        let parent_name = variant.parent_enum(db).name(db).to_string();

        let name = format!("{}::{}", parent_name, variant.name(db));

        let mut params = vec![];
        let mut parameter_types = vec![];
        for field in variant.fields(db).into_iter() {
            let ty = field.signature_ty(db);
            let raw_param = format!("{}", ty.display(db));
            if let Some(param_type) = raw_param.split(':').nth(1) {
                parameter_types.push(param_type[1..].to_string());
            } else {
                // The unwrap_or_else is useful when you have tuple
                parameter_types.push(raw_param);
            }
            let name = field.name(db);

            params.push(format!("{}: {}", name, ty.display(db)));
        }

        Some(
            FunctionSignature {
                kind: CallableKind::VariantConstructor,
                visibility: None,
                // Do we need `const`?
                qualifier: Default::default(),
                name: Some(name),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types,
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(variant.docs(db)),
        )
    }

    pub(crate) fn from_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
        let node: ast::MacroCall = macro_def.source(db).value;

        let params = vec![];

        Some(
            FunctionSignature {
                kind: CallableKind::Macro,
                visibility: None,
                qualifier: Default::default(),
                name: node.name().map(|n| n.text().to_string()),
                ret_type: None,
                parameters: params,
                parameter_names: vec![],
                parameter_types: vec![],
                generic_parameters: vec![],
                where_predicates: vec![],
                doc: None,
                has_self_param: false,
            }
            .with_doc_opt(macro_def.docs(db)),
        )
    }
}

impl From<&'_ ast::FnDef> for FunctionSignature {
    fn from(node: &ast::FnDef) -> FunctionSignature {
        fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
            let mut res = vec![];
            let mut res_types = vec![];
            let mut has_self_param = false;
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    has_self_param = true;
                    let raw_param = self_param.syntax().text().to_string();

                    res_types.push(
                        raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(),
                    );
                    res.push(raw_param);
                }

                res.extend(param_list.params().map(|param| param.syntax().text().to_string()));
                res_types.extend(param_list.params().map(|param| {
                    param.syntax().text().to_string().split(':').nth(1).unwrap()[1..].to_string()
                }));
            }
            (has_self_param, res, res_types)
        }

        fn param_name_list(node: &ast::FnDef) -> Vec<String> {
            let mut res = vec![];
            if let Some(param_list) = node.param_list() {
                if let Some(self_param) = param_list.self_param() {
                    res.push(self_param.syntax().text().to_string())
                }

                res.extend(
                    param_list
                        .params()
                        .map(|param| {
                            Some(
                                param
                                    .pat()?
                                    .syntax()
                                    .descendants()
                                    .find_map(ast::Name::cast)?
                                    .text()
                                    .to_string(),
                            )
                        })
                        .map(|param| param.unwrap_or_default()),
                );
            }
            res
        }

        let (has_self_param, parameters, parameter_types) = param_list(node);

        FunctionSignature {
            kind: CallableKind::Function,
            visibility: node.visibility().map(|n| n.syntax().text().to_string()),
            qualifier: FunctionQualifier {
                is_async: node.async_token().is_some(),
                is_const: node.const_token().is_some(),
                is_unsafe: node.unsafe_token().is_some(),
                extern_abi: node.abi().map(|n| n.to_string()),
            },
            name: node.name().map(|n| n.text().to_string()),
            ret_type: node
                .ret_type()
                .and_then(|r| r.type_ref())
                .map(|n| n.syntax().text().to_string()),
            parameters,
            parameter_names: param_name_list(node),
            parameter_types,
            generic_parameters: generic_parameters(node),
            where_predicates: where_predicates(node),
            // docs are processed separately
            doc: None,
            has_self_param,
        }
    }
}

impl Display for FunctionSignature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(t) = &self.visibility {
            write!(f, "{} ", t)?;
        }

        if self.qualifier.is_async {
            write!(f, "async ")?;
        }

        if self.qualifier.is_const {
            write!(f, "const ")?;
        }

        if self.qualifier.is_unsafe {
            write!(f, "unsafe ")?;
        }

        if let Some(extern_abi) = &self.qualifier.extern_abi {
            // Keyword `extern` is included in the string.
            write!(f, "{} ", extern_abi)?;
        }

        if let Some(name) = &self.name {
            match self.kind {
                CallableKind::Function => write!(f, "fn {}", name)?,
                CallableKind::StructConstructor => write!(f, "struct {}", name)?,
                CallableKind::VariantConstructor => write!(f, "{}", name)?,
                CallableKind::Macro => write!(f, "{}!", name)?,
            }
        }

        if !self.generic_parameters.is_empty() {
            write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
        }

        write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;

        if let Some(t) = &self.ret_type {
            write!(f, " -> {}", t)?;
        }

        if !self.where_predicates.is_empty() {
            write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n      "))?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate gl;

use std::ffi::CString;
use std::ptr;
use super::{Shader, ShaderType};

/// Wrapper around a linked OpenGL program
///
/// Created using `ProgramBuilder` or `ProgramFromFileBuilder`
pub struct Program {
    handle: u32,
}

impl Program {
    pub fn handle(&self) -> u32 {
        return self.handle;
    }
}

impl Drop for Program {
    fn drop (&mut self) {
        unsafe { gl::DeleteProgram(self.handle); }
    }
}

/// A builder class for linking a program using compiled shaders
///
/// # Example
/// ```no_run
/// use engine::gliw::{Shader, ShaderType, ProgramBuilder};
///
/// let compiled_vs = Shader::new(ShaderType::Vertex, "<code>").unwrap();
/// let compiled_fs = Shader::new(ShaderType::Fragment, "<code>").unwrap();
///
/// let prog = ProgramBuilder::new()
///     .attach_vs(&compiled_vs)
///     .attach_fs(&compiled_fs)
///     .link()
///     .unwrap();
/// ```
pub struct ProgramBuilder<'a> {
    cs: Option<&'a Shader>,
    vs: Option<&'a Shader>,
    tcs: Option<&'a Shader>,
    tes: Option<&'a Shader>,
    gs: Option<&'a Shader>,
    fs: Option<&'a Shader>,
}

impl<'a> ProgramBuilder<'a> {
    pub fn new() -> ProgramBuilder<'a> {
        return ProgramBuilder {
            cs: None,
            vs: None,
            tcs: None,
            tes: None,
            gs: None,
            fs: None,
        }
    }

    /// Set compute shader to attach
    pub fn attach_cs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        // TODO: check if shader is compute shader?
        self.cs = Some(shader);
        return self;
    }

    /// Set vertex shader to attach
    pub fn attach_vs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.vs = Some(shader);
        return self;
    }

    /// Set tesselation control shader to attach
    pub fn attach_tcs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.tcs = Some(shader);
        return self;
    }

    /// Set tesselation evaluation shader to attach
    pub fn attach_tes (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.tes = Some(shader);
        return self;
    }

    /// Set geometry shader to attach
    pub fn attach_gs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.gs = Some(shader);
        return self;
    }

    /// Set fragment shader to attach
    pub fn attach_fs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.fs = Some(shader);
        return self;
    }

    /// Links a program object using the attached shaders
    pub fn link(&self) -> Result<Program, String> {
        unsafe {
            let prog = gl::CreateProgram();

            if let Some(shader) = self.cs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.vs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.tcs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.tes { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.gs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.fs { gl::AttachShader(prog, shader.handle()); }

            gl::LinkProgram(prog);

            let mut status: i32 = 0;
            gl::GetProgramiv(prog, gl::LINK_STATUS, &mut status);
            if status != (gl::TRUE as i32) {
                let mut log_size: i32 = 0;
                gl::GetProgramiv(prog, gl::INFO_LOG_LENGTH, &mut log_size);

                let buff = CString::from_vec_unchecked(vec![0u8; log_size as usize]);
                gl::GetProgramInfoLog(prog, log_size, ptr::null_mut(), buff.as_ptr() as *mut _);

                gl::DeleteProgram(prog);
                return Err(buff.to_str().unwrap().to_string());
            }

            if let Some(shader) = self.cs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.vs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.tcs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.tes { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.gs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.fs { gl::DetachShader(prog, shader.handle()); }

            return Ok(Program{
                handle: prog,
            });
        }
    }
}

/// An utility builder class for compiling and linking a program using shader code from files
///
/// # Example
/// ```no_run
/// use engine::gliw::ProgramFromFileBuilder;
///
/// let prog = ProgramFromFileBuilder::new()
///     .vs_path("file.vs")
///     .fs_path("file.fs")
///     .compile()
///     .unwrap();
/// ```
pub struct ProgramFromFileBuilder<'a> {
    cs_path: Option<&'a str>,
    vs_path: Option<&'a str>,
    tcs_path: Option<&'a str>,
    tes_path: Option<&'a str>,
    gs_path: Option<&'a str>,
    fs_path: Option<&'a str>,
}

impl<'a> ProgramFromFileBuilder<'a> {
    pub fn new() -> ProgramFromFileBuilder<'a> {
        ProgramFromFileBuilder {
            cs_path: None,
            vs_path: None,
            tcs_path: None,
            tes_path: None,
            gs_path: None,
            fs_path: None,
        }
    }

    /// Set the file containing compute shader code
    pub fn cs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.cs_path = Some(path);
        self
    }

    /// Set the file containing vertex shader code
    pub fn vs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.vs_path = Some(path);
        self
    }

    /// Set the file containing tesselation control shader code
    pub fn tcs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.tcs_path = Some(path);
        self
    }

    /// Set the file containing tesselation evaluation shader code
    pub fn tes_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.tes_path = Some(path);
        self
    }

    /// Set the file containing geometry shader code
    pub fn gs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.gs_path = Some(path);
        self
    }

    /// Set the file containing fragment shader code
    pub fn fs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.fs_path = Some(path);
        self
    }

    /// Compiles the provided shaders and links them into a program
    pub fn compile(&self) -> Result<Program, String> {
        let cs: Shader;
        let vs: Shader;
        let tcs: Shader;
        let tes: Shader;
        let gs: Shader;
        let fs: Shader;

        let mut prog_builder = ProgramBuilder::new();

        if let Some(filename) = self.cs_path {
            match Shader::from_file(ShaderType::Compute, filename) {
                Ok(res) => { cs = res; prog_builder.cs = Some(&cs); },
                Err(err) => { return Err(format!("failed compilation of compute shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.vs_path {
            match Shader::from_file(ShaderType::Vertex, filename) {
                Ok(res) => { vs = res; prog_builder.vs = Some(&vs); },
                Err(err) => { return Err(format!("failed compilation of vertex shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.tcs_path {
            match Shader::from_file(ShaderType::TessControl, filename) {
                Ok(res) => { tcs = res; prog_builder.tcs = Some(&tcs); },
                Err(err) => { return Err(format!("failed compilation of tesselation control shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.tes_path {
            match Shader::from_file(ShaderType::TessEvaluation, filename) {
                Ok(res) => { tes = res; prog_builder.tes = Some(&tes); },
                Err(err) => { return Err(format!("failed compilation of tesselation evaluation shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.gs_path {
            match Shader::from_file(ShaderType::Geometry, filename) {
                Ok(res) => { gs = res; prog_builder.gs = Some(&gs); },
                Err(err) => { return Err(format!("failed compilation of geometry shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.fs_path {
            match Shader::from_file(ShaderType::Fragment, filename) {
                Ok(res) => { fs = res; prog_builder.fs = Some(&fs); },
                Err(err) => { return Err(format!("failed compilation of framgent shader {}:\n{}", filename, err)); }
            }
        }

        return prog_builder.link();
    }
}
<commit_msg>Consistency: changed "use super::{...}" to "use gliw::{...}"<commit_after>extern crate gl;

use std::ffi::CString;
use std::ptr;
use gliw::{Shader, ShaderType};

/// Wrapper around a linked OpenGL program
///
/// Created using `ProgramBuilder` or `ProgramFromFileBuilder`
pub struct Program {
    handle: u32,
}

impl Program {
    pub fn handle(&self) -> u32 {
        return self.handle;
    }
}

impl Drop for Program {
    fn drop (&mut self) {
        unsafe { gl::DeleteProgram(self.handle); }
    }
}

/// A builder class for linking a program using compiled shaders
///
/// # Example
/// ```no_run
/// use engine::gliw::{Shader, ShaderType, ProgramBuilder};
///
/// let compiled_vs = Shader::new(ShaderType::Vertex, "<code>").unwrap();
/// let compiled_fs = Shader::new(ShaderType::Fragment, "<code>").unwrap();
///
/// let prog = ProgramBuilder::new()
///     .attach_vs(&compiled_vs)
///     .attach_fs(&compiled_fs)
///     .link()
///     .unwrap();
/// ```
pub struct ProgramBuilder<'a> {
    cs: Option<&'a Shader>,
    vs: Option<&'a Shader>,
    tcs: Option<&'a Shader>,
    tes: Option<&'a Shader>,
    gs: Option<&'a Shader>,
    fs: Option<&'a Shader>,
}

impl<'a> ProgramBuilder<'a> {
    pub fn new() -> ProgramBuilder<'a> {
        return ProgramBuilder {
            cs: None,
            vs: None,
            tcs: None,
            tes: None,
            gs: None,
            fs: None,
        }
    }

    /// Set compute shader to attach
    pub fn attach_cs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        // TODO: check if shader is compute shader?
        self.cs = Some(shader);
        return self;
    }

    /// Set vertex shader to attach
    pub fn attach_vs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.vs = Some(shader);
        return self;
    }

    /// Set tesselation control shader to attach
    pub fn attach_tcs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.tcs = Some(shader);
        return self;
    }

    /// Set tesselation evaluation shader to attach
    pub fn attach_tes (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.tes = Some(shader);
        return self;
    }

    /// Set geometry shader to attach
    pub fn attach_gs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.gs = Some(shader);
        return self;
    }

    /// Set fragment shader to attach
    pub fn attach_fs (&mut self, shader: &'a Shader) -> &'a mut ProgramBuilder {
        self.fs = Some(shader);
        return self;
    }

    /// Links a program object using the attached shaders
    pub fn link(&self) -> Result<Program, String> {
        unsafe {
            let prog = gl::CreateProgram();

            if let Some(shader) = self.cs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.vs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.tcs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.tes { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.gs { gl::AttachShader(prog, shader.handle()); }
            if let Some(shader) = self.fs { gl::AttachShader(prog, shader.handle()); }

            gl::LinkProgram(prog);

            let mut status: i32 = 0;
            gl::GetProgramiv(prog, gl::LINK_STATUS, &mut status);
            if status != (gl::TRUE as i32) {
                let mut log_size: i32 = 0;
                gl::GetProgramiv(prog, gl::INFO_LOG_LENGTH, &mut log_size);

                let buff = CString::from_vec_unchecked(vec![0u8; log_size as usize]);
                gl::GetProgramInfoLog(prog, log_size, ptr::null_mut(), buff.as_ptr() as *mut _);

                gl::DeleteProgram(prog);
                return Err(buff.to_str().unwrap().to_string());
            }

            if let Some(shader) = self.cs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.vs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.tcs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.tes { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.gs { gl::DetachShader(prog, shader.handle()); }
            if let Some(shader) = self.fs { gl::DetachShader(prog, shader.handle()); }

            return Ok(Program{
                handle: prog,
            });
        }
    }
}

/// An utility builder class for compiling and linking a program using shader code from files
///
/// # Example
/// ```no_run
/// use engine::gliw::ProgramFromFileBuilder;
///
/// let prog = ProgramFromFileBuilder::new()
///     .vs_path("file.vs")
///     .fs_path("file.fs")
///     .compile()
///     .unwrap();
/// ```
pub struct ProgramFromFileBuilder<'a> {
    cs_path: Option<&'a str>,
    vs_path: Option<&'a str>,
    tcs_path: Option<&'a str>,
    tes_path: Option<&'a str>,
    gs_path: Option<&'a str>,
    fs_path: Option<&'a str>,
}

impl<'a> ProgramFromFileBuilder<'a> {
    pub fn new() -> ProgramFromFileBuilder<'a> {
        ProgramFromFileBuilder {
            cs_path: None,
            vs_path: None,
            tcs_path: None,
            tes_path: None,
            gs_path: None,
            fs_path: None,
        }
    }

    /// Set the file containing compute shader code
    pub fn cs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.cs_path = Some(path);
        self
    }

    /// Set the file containing vertex shader code
    pub fn vs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.vs_path = Some(path);
        self
    }

    /// Set the file containing tesselation control shader code
    pub fn tcs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.tcs_path = Some(path);
        self
    }

    /// Set the file containing tesselation evaluation shader code
    pub fn tes_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.tes_path = Some(path);
        self
    }

    /// Set the file containing geometry shader code
    pub fn gs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.gs_path = Some(path);
        self
    }

    /// Set the file containing fragment shader code
    pub fn fs_path(&mut self, path: &'a str) -> &'a mut ProgramFromFileBuilder {
        self.fs_path = Some(path);
        self
    }

    /// Compiles the provided shaders and links them into a program
    pub fn compile(&self) -> Result<Program, String> {
        let cs: Shader;
        let vs: Shader;
        let tcs: Shader;
        let tes: Shader;
        let gs: Shader;
        let fs: Shader;

        let mut prog_builder = ProgramBuilder::new();

        if let Some(filename) = self.cs_path {
            match Shader::from_file(ShaderType::Compute, filename) {
                Ok(res) => { cs = res; prog_builder.cs = Some(&cs); },
                Err(err) => { return Err(format!("failed compilation of compute shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.vs_path {
            match Shader::from_file(ShaderType::Vertex, filename) {
                Ok(res) => { vs = res; prog_builder.vs = Some(&vs); },
                Err(err) => { return Err(format!("failed compilation of vertex shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.tcs_path {
            match Shader::from_file(ShaderType::TessControl, filename) {
                Ok(res) => { tcs = res; prog_builder.tcs = Some(&tcs); },
                Err(err) => { return Err(format!("failed compilation of tesselation control shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.tes_path {
            match Shader::from_file(ShaderType::TessEvaluation, filename) {
                Ok(res) => { tes = res; prog_builder.tes = Some(&tes); },
                Err(err) => { return Err(format!("failed compilation of tesselation evaluation shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.gs_path {
            match Shader::from_file(ShaderType::Geometry, filename) {
                Ok(res) => { gs = res; prog_builder.gs = Some(&gs); },
                Err(err) => { return Err(format!("failed compilation of geometry shader {}:\n{}", filename, err)); }
            }
        }

        if let Some(filename) = self.fs_path {
            match Shader::from_file(ShaderType::Fragment, filename) {
                Ok(res) => { fs = res; prog_builder.fs = Some(&fs); },
                Err(err) => { return Err(format!("failed compilation of framgent shader {}:\n{}", filename, err)); }
            }
        }

        return prog_builder.link();
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The task that handles all rendering/painting.

use buffer_map::BufferMap;
use display_list::optimizer::DisplayListOptimizer;
use display_list::DisplayList;
use font_context::FontContext;
use render_context::RenderContext;

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers;
use native;
use rustrt::task;
use rustrt::task::TaskOpts;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerId};
use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry;
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use sync::Arc;
use font_cache_task::FontCacheTask;

/// Information about a layer that layout sends to the painting task.
pub struct RenderLayer {
    /// A per-pipeline ID describing this layer that should be stable across reflows.
    pub id: LayerId,
    /// The display list describing the contents of this layer.
    pub display_list: Arc<DisplayList>,
    /// The position of the layer in pixels.
    pub position: Rect<uint>,
    /// The color of the background in this layer. Used for unrendered content.
    pub background_color: Color,
    /// The scrolling policy of this layer.
    pub scroll_policy: ScrollPolicy,
}

pub struct ReRenderRequest {
    pub buffer_requests: Vec<BufferRequest>,
    pub scale: f32,
    pub layer_id: LayerId,
    pub epoch: Epoch,
}

pub enum Msg {
    RenderMsg(SmallVec1<RenderLayer>),
    ReRenderMsg(Vec<ReRenderRequest>),
    UnusedBufferMsg(Vec<Box<LayerBuffer>>),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Option<Sender<()>>),
}

#[deriving(Clone)]
pub struct RenderChan(Sender<Msg>);

impl RenderChan {
    pub fn new() -> (Receiver<Msg>, RenderChan) {
        let (chan, port) = channel();
        (port, RenderChan(chan))
    }

    pub fn send(&self, msg: Msg) {
        let &RenderChan(ref chan) = self;
        assert!(chan.send_opt(msg).is_ok(), "RenderChan.send: render port closed")
    }

    pub fn send_opt(&self, msg: Msg) -> Result<(), Msg> {
        let &RenderChan(ref chan) = self;
        chan.send_opt(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
pub enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C> {
    id: PipelineId,
    port: Receiver<Msg>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: Box<FontContext>,
    opts: Opts,

    /// A channel to the time profiler.
    time_profiler_chan: TimeProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layers to be rendered.
    render_layers: SmallVec1<RenderLayer>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap<Box<LayerBuffer>>,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

fn initialize_layers<C:RenderListener>(
                     compositor: &mut C,
                     pipeline_id: PipelineId,
                     epoch: Epoch,
                     render_layers: &[RenderLayer]) {
    let metadata = render_layers.iter().map(|render_layer| {
        LayerMetadata {
            id: render_layer.id,
            position: render_layer.position,
            background_color: render_layer.background_color,
            scroll_policy: render_layer.scroll_policy,
        }
    }).collect();
    compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
}

impl<C:RenderListener + Send> RenderTask<C> {
    pub fn create(id: PipelineId,
                  port: Receiver<Msg>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  font_cache_task: FontCacheTask,
                  failure_msg: Failure,
                  opts: Opts,
                  time_profiler_chan: TimeProfilerChan,
                  shutdown_chan: Sender<()>) {

        let ConstellationChan(c) = constellation_chan.clone();
        let fc = font_cache_task.clone();

        let mut task_opts = TaskOpts::new();
        task_opts.name = Some("RenderTask".into_maybe_owned());
        task_opts.on_exit = Some(proc(result: task::Result) {
            match result {
                Ok(()) => {},
                Err(..) => {
                    c.send(FailureMsg(failure_msg));
                }
            }
        });
        native::task::spawn_opts(task_opts, proc() {
            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: box FontContext::new(fc.clone()),
                    opts: opts,
                    time_profiler_chan: time_profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layers: SmallVec1::new(),

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                match render_task.native_graphics_context.as_ref() {
                    Some(ctx) => render_task.buffer_map.clear(ctx),
                    None => (),
                }
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layers) => {
                    self.epoch.next();
                    self.render_layers = render_layers;

                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    initialize_layers(&mut self.compositor,
                                      self.id,
                                      self.epoch,
                                      self.render_layers.as_slice());
                }
                ReRenderMsg(requests) => {
                    for ReRenderRequest { buffer_requests, scale, layer_id, epoch }
                          in requests.move_iter() {
                        if self.epoch == epoch {
                            self.render(buffer_requests, scale, layer_id);
                        } else {
                            debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                        }
                    }
                }
                UnusedBufferMsg(unused_buffers) => {
                    for buffer in unused_buffers.move_iter().rev() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;

                    // Here we assume that the main layer—the layer responsible for the page size—
                    // is the first layer. This is a pretty fragile assumption. It will be fixed
                    // once we use the layers-based scrolling infrastructure for all scrolling.
                    if self.render_layers.len() > 1 {
                        self.epoch.next();
                        initialize_layers(&mut self.compositor,
                                          self.id,
                                          self.epoch,
                                          self.render_layers.as_slice());
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.map(|ch| ch.send(()));
                    break;
                }
            }
        }
    }

    /// Renders one layer and sends the tiles back to the layer.
    ///
    /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
    /// one transaction, to avoid the user seeing inconsistent states.
    fn render(&mut self, tiles: Vec<BufferRequest>, scale: f32, layer_id: LayerId) {
        time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = vec!();

            // Find the appropriate render layer.
            let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
                Some(render_layer) => render_layer,
                None => return,
            };

            self.compositor.set_render_state(RenderingRenderState);

            // Divide up the layer into tiles.
            for tile in tiles.iter() {
                // Optimize the display list for this tile.
                let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect);
                let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(),
                                                          page_rect_au);
                let display_list = optimizer.optimize();

                let width = tile.screen_rect.size.width;
                let height = tile.screen_rect.size.height;

                let size = Size2D(width as i32, height as i32);
                let draw_target = match self.graphics_context {
                    CpuGraphicsContext => {
                        DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                    }
                    GpuGraphicsContext => {
                        // FIXME(pcwalton): Cache the components of draw targets
                        // (texture color buffer, renderbuffers) instead of recreating them.
                        let draw_target =
                            DrawTarget::new_with_fbo(self.opts.render_backend,
                                                     native_graphics_context!(self),
                                                     size,
                                                     B8G8R8A8);
                        draw_target.make_current();
                        draw_target
                    }
                };

                {
                    // Build the render context.
                    let mut ctx = RenderContext {
                        draw_target: &draw_target,
                        font_ctx: &mut self.font_ctx,
                        opts: &self.opts,
                        page_rect: tile.page_rect,
                        screen_rect: tile.screen_rect,
                    };

                    // Apply the translation to render the tile we want.
                    let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                    let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                    let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                  -(tile.page_rect.origin.y) as AzFloat);
                    let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
                                                  -(render_layer.position.origin.y as AzFloat));

                    ctx.draw_target.set_transform(&matrix);

                    // Clear the buffer.
                    ctx.clear();

                    // Draw the display list.
                    profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
                        display_list.draw_into_context(&mut ctx);
                        ctx.draw_target.flush();
                    });
                }

                // Extract the texture from the draw target and place it into its slot in the
                // buffer. If using CPU rendering, upload it first.
                //
                // FIXME(pcwalton): We should supply the texture and native surface *to* the
                // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                let buffer = match self.graphics_context {
                    CpuGraphicsContext => {
                        let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                            Some(buffer) => {
                                let mut buffer = buffer;
                                buffer.rect = tile.page_rect;
                                buffer.screen_pos = tile.screen_rect;
                                buffer.resolution = scale;
                                buffer.native_surface.mark_wont_leak();
                                buffer
                            }
                            None => {
                                // Create an empty native surface. We mark it as not leaking
                                // in case it dies in transit to the compositor task.
                                let mut native_surface: NativeSurface =
                                    layers::platform::surface::NativeSurfaceMethods::new(
                                        native_graphics_context!(self),
                                        Size2D(width as i32, height as i32),
                                        width as i32 * 4);
                                native_surface.mark_wont_leak();

                                box LayerBuffer {
                                    native_surface: native_surface,
                                    rect: tile.page_rect,
                                    screen_pos: tile.screen_rect,
                                    resolution: scale,
                                    stride: (width * 4) as uint
                                }
                            }
                        };

                        draw_target.snapshot().get_data_surface().with_data(|data| {
                            buffer.native_surface.upload(native_graphics_context!(self), data);
                            debug!("RENDERER uploading to native surface {:d}",
                                   buffer.native_surface.get_id() as int);
                        });

                        buffer
                    }
                    GpuGraphicsContext => {
                        draw_target.make_current();
                        let StolenGLResources {
                            surface: native_surface
                        } = draw_target.steal_gl_resources().unwrap();

                        // We mark the native surface as not leaking in case the surfaces
                        // die on their way to the compositor task.
                        let mut native_surface: NativeSurface =
                            NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                        native_surface.mark_wont_leak();

                        box LayerBuffer {
                            native_surface: native_surface,
                            rect: tile.page_rect,
                            screen_pos: tile.screen_rect,
                            resolution: scale,
                            stride: (width * 4) as uint
                        }
                    }
                };

                new_buffers.push(buffer);
            }

            let layer_buffer_set = box LayerBufferSet {
                buffers: new_buffers,
            };

            debug!("render_task: returning surface");
            if self.paint_permission {
                self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch);
            } else {
                debug!("render_task: RendererReadyMsg send");
                let ConstellationChan(ref mut c) = self.constellation_chan;
                c.send(RendererReadyMsg(self.id));
            }
            self.compositor.set_render_state(IdleRenderState);
        })
    }
}

<commit_msg>Check whether the render task has permission to paint before painting<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The task that handles all rendering/painting.

use buffer_map::BufferMap;
use display_list::optimizer::DisplayListOptimizer;
use display_list::DisplayList;
use font_context::FontContext;
use render_context::RenderContext;

use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers;
use native;
use rustrt::task;
use rustrt::task::TaskOpts;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerId};
use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry;
use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{TimeProfilerChan, profile};
use servo_util::time;
use std::comm::{Receiver, Sender, channel};
use sync::Arc;
use font_cache_task::FontCacheTask;

/// Information about a layer that layout sends to the painting task.
pub struct RenderLayer {
    /// A per-pipeline ID describing this layer that should be stable across reflows.
    pub id: LayerId,
    /// The display list describing the contents of this layer.
    pub display_list: Arc<DisplayList>,
    /// The position of the layer in pixels.
    pub position: Rect<uint>,
    /// The color of the background in this layer. Used for unrendered content.
    pub background_color: Color,
    /// The scrolling policy of this layer.
    pub scroll_policy: ScrollPolicy,
}

pub struct ReRenderRequest {
    pub buffer_requests: Vec<BufferRequest>,
    pub scale: f32,
    pub layer_id: LayerId,
    pub epoch: Epoch,
}

pub enum Msg {
    RenderMsg(SmallVec1<RenderLayer>),
    ReRenderMsg(Vec<ReRenderRequest>),
    UnusedBufferMsg(Vec<Box<LayerBuffer>>),
    PaintPermissionGranted,
    PaintPermissionRevoked,
    ExitMsg(Option<Sender<()>>),
}

#[deriving(Clone)]
pub struct RenderChan(Sender<Msg>);

impl RenderChan {
    pub fn new() -> (Receiver<Msg>, RenderChan) {
        let (chan, port) = channel();
        (port, RenderChan(chan))
    }

    pub fn send(&self, msg: Msg) {
        let &RenderChan(ref chan) = self;
        assert!(chan.send_opt(msg).is_ok(), "RenderChan.send: render port closed")
    }

    pub fn send_opt(&self, msg: Msg) -> Result<(), Msg> {
        let &RenderChan(ref chan) = self;
        chan.send_opt(msg)
    }
}

/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
pub enum GraphicsContext {
    CpuGraphicsContext,
    GpuGraphicsContext,
}

pub struct RenderTask<C> {
    id: PipelineId,
    port: Receiver<Msg>,
    compositor: C,
    constellation_chan: ConstellationChan,
    font_ctx: Box<FontContext>,
    opts: Opts,

    /// A channel to the time profiler.
    time_profiler_chan: TimeProfilerChan,

    /// The graphics context to use.
    graphics_context: GraphicsContext,

    /// The native graphics context.
    native_graphics_context: Option<NativePaintingGraphicsContext>,

    /// The layers to be rendered.
    render_layers: SmallVec1<RenderLayer>,

    /// Permission to send paint messages to the compositor
    paint_permission: bool,

    /// A counter for epoch messages
    epoch: Epoch,

    /// A data structure to store unused LayerBuffers
    buffer_map: BufferMap<Box<LayerBuffer>>,
}

// If we implement this as a function, we get borrowck errors from borrowing
// the whole RenderTask struct.
macro_rules! native_graphics_context(
    ($task:expr) => (
        $task.native_graphics_context.as_ref().expect("Need a graphics context to do rendering")
    )
)

fn initialize_layers<C:RenderListener>(
                     compositor: &mut C,
                     pipeline_id: PipelineId,
                     epoch: Epoch,
                     render_layers: &[RenderLayer]) {
    let metadata = render_layers.iter().map(|render_layer| {
        LayerMetadata {
            id: render_layer.id,
            position: render_layer.position,
            background_color: render_layer.background_color,
            scroll_policy: render_layer.scroll_policy,
        }
    }).collect();
    compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
}

impl<C:RenderListener + Send> RenderTask<C> {
    pub fn create(id: PipelineId,
                  port: Receiver<Msg>,
                  compositor: C,
                  constellation_chan: ConstellationChan,
                  font_cache_task: FontCacheTask,
                  failure_msg: Failure,
                  opts: Opts,
                  time_profiler_chan: TimeProfilerChan,
                  shutdown_chan: Sender<()>) {

        let ConstellationChan(c) = constellation_chan.clone();
        let fc = font_cache_task.clone();

        let mut task_opts = TaskOpts::new();
        task_opts.name = Some("RenderTask".into_maybe_owned());
        task_opts.on_exit = Some(proc(result: task::Result) {
            match result {
                Ok(()) => {},
                Err(..) => {
                    c.send(FailureMsg(failure_msg));
                }
            }
        });
        native::task::spawn_opts(task_opts, proc() {
            { // Ensures RenderTask and graphics context are destroyed before shutdown msg
                let native_graphics_context = compositor.get_graphics_metadata().map(
                    |md| NativePaintingGraphicsContext::from_metadata(&md));
                let cpu_painting = opts.cpu_painting;

                // FIXME: rust/#5967
                let mut render_task = RenderTask {
                    id: id,
                    port: port,
                    compositor: compositor,
                    constellation_chan: constellation_chan,
                    font_ctx: box FontContext::new(fc.clone()),
                    opts: opts,
                    time_profiler_chan: time_profiler_chan,

                    graphics_context: if cpu_painting {
                        CpuGraphicsContext
                    } else {
                        GpuGraphicsContext
                    },

                    native_graphics_context: native_graphics_context,

                    render_layers: SmallVec1::new(),

                    paint_permission: false,
                    epoch: Epoch(0),
                    buffer_map: BufferMap::new(10000000),
                };

                render_task.start();

                // Destroy all the buffers.
                match render_task.native_graphics_context.as_ref() {
                    Some(ctx) => render_task.buffer_map.clear(ctx),
                    None => (),
                }
            }

            debug!("render_task: shutdown_chan send");
            shutdown_chan.send(());
        });
    }

    fn start(&mut self) {
        debug!("render_task: beginning rendering loop");

        loop {
            match self.port.recv() {
                RenderMsg(render_layers) => {
                    self.epoch.next();
                    self.render_layers = render_layers;

                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    initialize_layers(&mut self.compositor,
                                      self.id,
                                      self.epoch,
                                      self.render_layers.as_slice());
                }
                ReRenderMsg(requests) => {
                    if !self.paint_permission {
                        debug!("render_task: render ready msg");
                        let ConstellationChan(ref mut c) = self.constellation_chan;
                        c.send(RendererReadyMsg(self.id));
                        continue;
                    }

                    for ReRenderRequest { buffer_requests, scale, layer_id, epoch }
                          in requests.move_iter() {
                        if self.epoch == epoch {
                            self.render(buffer_requests, scale, layer_id);
                        } else {
                            debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
                        }
                    }
                }
                UnusedBufferMsg(unused_buffers) => {
                    for buffer in unused_buffers.move_iter().rev() {
                        self.buffer_map.insert(native_graphics_context!(self), buffer);
                    }
                }
                PaintPermissionGranted => {
                    self.paint_permission = true;

                    // Here we assume that the main layer—the layer responsible for the page size—
                    // is the first layer. This is a pretty fragile assumption. It will be fixed
                    // once we use the layers-based scrolling infrastructure for all scrolling.
                    if self.render_layers.len() > 1 {
                        self.epoch.next();
                        initialize_layers(&mut self.compositor,
                                          self.id,
                                          self.epoch,
                                          self.render_layers.as_slice());
                    }
                }
                PaintPermissionRevoked => {
                    self.paint_permission = false;
                }
                ExitMsg(response_ch) => {
                    debug!("render_task: exitmsg response send");
                    response_ch.map(|ch| ch.send(()));
                    break;
                }
            }
        }
    }

    /// Renders one layer and sends the tiles back to the layer.
    ///
    /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
    /// one transaction, to avoid the user seeing inconsistent states.
    fn render(&mut self, tiles: Vec<BufferRequest>, scale: f32, layer_id: LayerId) {
        time::profile(time::RenderingCategory, self.time_profiler_chan.clone(), || {
            // FIXME: Try not to create a new array here.
            let mut new_buffers = vec!();

            // Find the appropriate render layer.
            let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
                Some(render_layer) => render_layer,
                None => return,
            };

            self.compositor.set_render_state(RenderingRenderState);

            // Divide up the layer into tiles.
            for tile in tiles.iter() {
                // Optimize the display list for this tile.
                let page_rect_au = geometry::f32_rect_to_au_rect(tile.page_rect);
                let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(),
                                                          page_rect_au);
                let display_list = optimizer.optimize();

                let width = tile.screen_rect.size.width;
                let height = tile.screen_rect.size.height;

                let size = Size2D(width as i32, height as i32);
                let draw_target = match self.graphics_context {
                    CpuGraphicsContext => {
                        DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
                    }
                    GpuGraphicsContext => {
                        // FIXME(pcwalton): Cache the components of draw targets
                        // (texture color buffer, renderbuffers) instead of recreating them.
                        let draw_target =
                            DrawTarget::new_with_fbo(self.opts.render_backend,
                                                     native_graphics_context!(self),
                                                     size,
                                                     B8G8R8A8);
                        draw_target.make_current();
                        draw_target
                    }
                };

                {
                    // Build the render context.
                    let mut ctx = RenderContext {
                        draw_target: &draw_target,
                        font_ctx: &mut self.font_ctx,
                        opts: &self.opts,
                        page_rect: tile.page_rect,
                        screen_rect: tile.screen_rect,
                    };

                    // Apply the translation to render the tile we want.
                    let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
                    let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
                    let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
                                                  -(tile.page_rect.origin.y) as AzFloat);
                    let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
                                                  -(render_layer.position.origin.y as AzFloat));

                    ctx.draw_target.set_transform(&matrix);

                    // Clear the buffer.
                    ctx.clear();

                    // Draw the display list.
                    profile(time::RenderingDrawingCategory, self.time_profiler_chan.clone(), || {
                        display_list.draw_into_context(&mut ctx);
                        ctx.draw_target.flush();
                    });
                }

                // Extract the texture from the draw target and place it into its slot in the
                // buffer. If using CPU rendering, upload it first.
                //
                // FIXME(pcwalton): We should supply the texture and native surface *to* the
                // draw target in GPU rendering mode, so that it doesn't have to recreate it.
                let buffer = match self.graphics_context {
                    CpuGraphicsContext => {
                        let buffer = match self.buffer_map.find(tile.screen_rect.size) {
                            Some(buffer) => {
                                let mut buffer = buffer;
                                buffer.rect = tile.page_rect;
                                buffer.screen_pos = tile.screen_rect;
                                buffer.resolution = scale;
                                buffer.native_surface.mark_wont_leak();
                                buffer
                            }
                            None => {
                                // Create an empty native surface. We mark it as not leaking
                                // in case it dies in transit to the compositor task.
                                let mut native_surface: NativeSurface =
                                    layers::platform::surface::NativeSurfaceMethods::new(
                                        native_graphics_context!(self),
                                        Size2D(width as i32, height as i32),
                                        width as i32 * 4);
                                native_surface.mark_wont_leak();

                                box LayerBuffer {
                                    native_surface: native_surface,
                                    rect: tile.page_rect,
                                    screen_pos: tile.screen_rect,
                                    resolution: scale,
                                    stride: (width * 4) as uint
                                }
                            }
                        };

                        draw_target.snapshot().get_data_surface().with_data(|data| {
                            buffer.native_surface.upload(native_graphics_context!(self), data);
                            debug!("RENDERER uploading to native surface {:d}",
                                   buffer.native_surface.get_id() as int);
                        });

                        buffer
                    }
                    GpuGraphicsContext => {
                        draw_target.make_current();
                        let StolenGLResources {
                            surface: native_surface
                        } = draw_target.steal_gl_resources().unwrap();

                        // We mark the native surface as not leaking in case the surfaces
                        // die on their way to the compositor task.
                        let mut native_surface: NativeSurface =
                            NativeSurfaceAzureMethods::from_azure_surface(native_surface);
                        native_surface.mark_wont_leak();

                        box LayerBuffer {
                            native_surface: native_surface,
                            rect: tile.page_rect,
                            screen_pos: tile.screen_rect,
                            resolution: scale,
                            stride: (width * 4) as uint
                        }
                    }
                };

                new_buffers.push(buffer);
            }

            let layer_buffer_set = box LayerBufferSet {
                buffers: new_buffers,
            };

            debug!("render_task: returning surface");
            self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch);
            self.compositor.set_render_state(IdleRenderState);
        })
    }
}

<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

/// Macro for early return with Ok dbus message on failure to get data
/// associated with object path.
macro_rules! get_data {
    ($path:ident; $default:expr; $message:expr) => {
        if let Some(ref data) = *$path.get_data() {
            data
        } else {
            let message = format!("no data for object path {}", $path.get_name());
            let (rc, rs) = (
                $crate::dbus_api::types::DbusErrorEnum::INTERNAL_ERROR as u16,
                message,
            );
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

/// Macro for early return with Ok dbus message on failure to get parent
/// object path from tree.
macro_rules! get_parent {
    ($m:ident; $data:ident; $default:expr; $message:expr) => {
        if let Some(parent) = $m.tree.get(&$data.parent) {
            parent
        } else {
            let message = format!("no path for object path {}", $data.parent);
            let (rc, rs) = (
                $crate::dbus_api::types::DbusErrorEnum::INTERNAL_ERROR as u16,
                message,
            );
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

/// Macro for early return with Ok dbus message on failure to get immutable pool.
macro_rules! get_pool {
    ($engine:ident; $uuid:ident; $default:expr; $message:expr) => {
        if let Some(pool) = $engine.get_pool($uuid) {
            pool
        } else {
            let message = format!("engine does not know about pool with uuid {}", $uuid);
            let (rc, rs) = (DbusErrorEnum::INTERNAL_ERROR as u16, message);
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

/// Macro for early return with Ok dbus message on failure to get mutable pool.
macro_rules! get_mut_pool {
    ($engine:ident; $uuid:ident; $default:expr; $message:expr) => {
        if let Some(pool) = $engine.get_mut_pool($uuid) {
            pool
        } else {
            let message = format!("engine does not know about pool with uuid {}", $uuid);
            let (rc, rs) = (
                $crate::dbus_api::types::DbusErrorEnum::INTERNAL_ERROR as u16,
                message,
            );
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

// Macro for formatting a Uuid object for transport on the D-Bus as a string
macro_rules! uuid_to_string {
    ($uuid:expr) => {
        $uuid.to_simple_ref().to_string()
    };
}

macro_rules! properties_footer {
    () => {
        pub fn get_all_properties(
            m: &dbus::tree::MethodInfo<
                dbus::tree::MTFn<$crate::dbus_api::types::TData>,
                $crate::dbus_api::types::TData,
            >,
        ) -> dbus::tree::MethodResult {
            get_properties_shared(m, &mut ALL_PROPERTIES.iter().map(|&s| s.to_string()))
        }

        pub fn get_properties(
            m: &dbus::tree::MethodInfo<
                dbus::tree::MTFn<$crate::dbus_api::types::TData>,
                $crate::dbus_api::types::TData,
            >,
        ) -> dbus::tree::MethodResult {
            let message: &dbus::Message = m.msg;
            let mut iter = message.iter_init();
            let mut properties: dbus::arg::Array<String, _> =
                $crate::dbus_api::util::get_next_arg(&mut iter, 0)?;
            get_properties_shared(m, &mut properties)
        }
    };
}

macro_rules! initial_properties {
    ($($iface:expr => { $($prop:expr => $val:expr),* }),*) => {{
        let mut interfaces = vec![
            $(
                ($iface, vec![
                    $(
                        ($prop, Variant(
                            Box::new($val) as Box<dyn dbus::arg::RefArg>
                        )),
                    )*
                ]
                .into_iter()
                .map(|(s, v): (&str, dbus::arg::Variant<Box<dyn dbus::arg::RefArg>>)| {
                    (s.to_string(), v)
                })
                .collect()),
            )*
        ]
        .into_iter()
        .map(|(s, v)| (s.to_string(), v))
        .collect::<$crate::dbus_api::types::InterfacesAdded>();
        interfaces.extend(
            $crate::dbus_api::consts::fetch_properties_interfaces()
                .into_iter()
                .map(|s| (s, std::collections::HashMap::new()))
        );
        interfaces
    }};
}
<commit_msg>Use fully qualified name in macro<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

/// Macro for early return with Ok dbus message on failure to get data
/// associated with object path.
macro_rules! get_data {
    ($path:ident; $default:expr; $message:expr) => {
        if let Some(ref data) = *$path.get_data() {
            data
        } else {
            let message = format!("no data for object path {}", $path.get_name());
            let (rc, rs) = (
                $crate::dbus_api::types::DbusErrorEnum::INTERNAL_ERROR as u16,
                message,
            );
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

/// Macro for early return with Ok dbus message on failure to get parent
/// object path from tree.
macro_rules! get_parent {
    ($m:ident; $data:ident; $default:expr; $message:expr) => {
        if let Some(parent) = $m.tree.get(&$data.parent) {
            parent
        } else {
            let message = format!("no path for object path {}", $data.parent);
            let (rc, rs) = (
                $crate::dbus_api::types::DbusErrorEnum::INTERNAL_ERROR as u16,
                message,
            );
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

/// Macro for early return with Ok dbus message on failure to get immutable pool.
macro_rules! get_pool {
    ($engine:ident; $uuid:ident; $default:expr; $message:expr) => {
        if let Some(pool) = $engine.get_pool($uuid) {
            pool
        } else {
            let message = format!("engine does not know about pool with uuid {}", $uuid);
            let (rc, rs) = (
                $crate::dbus_api::types::DbusErrorEnum::INTERNAL_ERROR as u16,
                message,
            );
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

/// Macro for early return with Ok dbus message on failure to get mutable pool.
macro_rules! get_mut_pool {
    ($engine:ident; $uuid:ident; $default:expr; $message:expr) => {
        if let Some(pool) = $engine.get_mut_pool($uuid) {
            pool
        } else {
            let message = format!("engine does not know about pool with uuid {}", $uuid);
            let (rc, rs) = (
                $crate::dbus_api::types::DbusErrorEnum::INTERNAL_ERROR as u16,
                message,
            );
            return Ok(vec![$message.append3($default, rc, rs)]);
        }
    };
}

// Macro for formatting a Uuid object for transport on the D-Bus as a string
macro_rules! uuid_to_string {
    ($uuid:expr) => {
        $uuid.to_simple_ref().to_string()
    };
}

macro_rules! properties_footer {
    () => {
        pub fn get_all_properties(
            m: &dbus::tree::MethodInfo<
                dbus::tree::MTFn<$crate::dbus_api::types::TData>,
                $crate::dbus_api::types::TData,
            >,
        ) -> dbus::tree::MethodResult {
            get_properties_shared(m, &mut ALL_PROPERTIES.iter().map(|&s| s.to_string()))
        }

        pub fn get_properties(
            m: &dbus::tree::MethodInfo<
                dbus::tree::MTFn<$crate::dbus_api::types::TData>,
                $crate::dbus_api::types::TData,
            >,
        ) -> dbus::tree::MethodResult {
            let message: &dbus::Message = m.msg;
            let mut iter = message.iter_init();
            let mut properties: dbus::arg::Array<String, _> =
                $crate::dbus_api::util::get_next_arg(&mut iter, 0)?;
            get_properties_shared(m, &mut properties)
        }
    };
}

macro_rules! initial_properties {
    ($($iface:expr => { $($prop:expr => $val:expr),* }),*) => {{
        let mut interfaces = vec![
            $(
                ($iface, vec![
                    $(
                        ($prop, Variant(
                            Box::new($val) as Box<dyn dbus::arg::RefArg>
                        )),
                    )*
                ]
                .into_iter()
                .map(|(s, v): (&str, dbus::arg::Variant<Box<dyn dbus::arg::RefArg>>)| {
                    (s.to_string(), v)
                })
                .collect()),
            )*
        ]
        .into_iter()
        .map(|(s, v)| (s.to_string(), v))
        .collect::<$crate::dbus_api::types::InterfacesAdded>();
        interfaces.extend(
            $crate::dbus_api::consts::fetch_properties_interfaces()
                .into_iter()
                .map(|s| (s, std::collections::HashMap::new()))
        );
        interfaces
    }};
}
<|endoftext|>


<commit_before>use std::iter::FromIterator;

#[derive(Clone, Eq, PartialEq, Debug)]
pub struct EncodingSchemeForm {
    pub prefix: u64,
    pub bit_count: u8,
    pub prefix_length: u8,
}

#[derive(Clone, Eq, PartialEq, Debug)]
pub struct EncodingScheme {
    bytes: Vec<u8>,
}

impl EncodingScheme {
    pub fn new(bytes: Vec<u8>) -> EncodingScheme {
        EncodingScheme { bytes: bytes }
    }

    pub fn bytes(&self) -> &Vec<u8> {
        &self.bytes
    }

    pub fn into_bytes(self) -> Vec<u8> {
        self.bytes
    }
}

impl IntoIterator for EncodingScheme {
    type Item = EncodingSchemeForm;
    type IntoIter = EncodingSchemeIterator;

    fn into_iter(self) -> EncodingSchemeIterator {
        EncodingSchemeIterator { bytes: self.bytes, window: 0, bits_in_window: 0, bytes_offset: 0 }
    }
}

#[derive(Debug)]
pub struct EncodingSchemeIterator {
    bytes: Vec<u8>,
    window: u64, // integer view of a slice of the buffer (in little endian)
    bits_in_window: u8,
    bytes_offset: usize,
}

impl Iterator for EncodingSchemeIterator {
    type Item = EncodingSchemeForm;

    fn next(&mut self) -> Option<EncodingSchemeForm> {
        // Load new bytes in the window, so it is larger than any possible form
        while self.bits_in_window < 5+5+0b11111 {
            let byte = *self.bytes.get(self.bytes_offset).unwrap_or(&0);
            self.window = self.window + ((byte as u64) << self.bits_in_window);
            self.bytes_offset += 1;
            self.bits_in_window += 8;
        }

        if self.window == 0 {
            None
        }
        else {
            assert!(self.bytes_offset <= self.bytes.len()+(5+5+0b11111)/8);
            let prefix_length = self.window as u8 & 0b11111;
            self.window >>= 5;
            self.bits_in_window -= 5;

            let bit_count = self.window as u8 & 0b11111;
            self.window >>= 5;
            self.bits_in_window -= 5;

            let prefix_mask = (0b1 << prefix_length) -1; // mask for the prefix_length last digits
            let prefix = self.window & prefix_mask;
            self.window >>= prefix_length;
            self.bits_in_window -= prefix_length;

            Some(EncodingSchemeForm { prefix: prefix, bit_count: bit_count, prefix_length: prefix_length })
        }
    }
}

impl<'a> FromIterator<&'a EncodingSchemeForm> for EncodingScheme {
    fn from_iter<T: IntoIterator<Item=&'a EncodingSchemeForm>>(forms: T) -> Self {
        let mut bytes = Vec::<u8>::new();
        let mut window = 0u64;
        let mut bits_in_window = 0u8;
        println!("---");
        for form in forms {
            assert!(form.prefix_length <= 0b11111);
            assert!(form.bit_count <= 0b11111);
            assert!(form.prefix < (1 << form.prefix_length));

            window += (form.prefix as u64) << (5+5+bits_in_window);
            window += (form.bit_count as u64) << (5+bits_in_window);
            window += (form.prefix_length as u64) << (bits_in_window);

            bits_in_window += 5+5+form.prefix_length;

            while bits_in_window >= 8 {
                bytes.push((window & 0b11111111) as u8);
                window >>= 8;
                bits_in_window -= 8;
            }
        }

        if bits_in_window > 0 {
            assert!(bits_in_window < 8);
            assert!(window <= 0b11111111);
            bytes.push(window as u8);
        }

        EncodingScheme::new(bytes)
    }
}

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

    #[test]
    fn test_trivial() {
        let encoding = vec![0b011_00000, 0b00];
        let forms = vec![EncodingSchemeForm { prefix: 0, bit_count: 3, prefix_length: 0 }];

        assert_eq!(EncodingScheme::new(encoding.clone()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::new(encoding.clone()).into_iter().collect::<Vec<_>>(), forms);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_iter().collect::<Vec<_>>(), forms);
    }

    /// From https://github.com/cjdelisle/cjdns/blob/cjdns-v18/switch/test/EncodingScheme_test.c#L121-L158
    #[test]
    fn test_three() {
        let encoding = b"\x4f\xf4\xff\x29\xd9\xff\x7f\x89\xee\xff\x07".to_vec();
        let forms = vec![
            EncodingSchemeForm {
                prefix_length: 15,
                bit_count: 2,
                prefix: ((1<<15)-1) ^ (1<<1),
            },
            EncodingSchemeForm {
                prefix_length: 20,
                bit_count: 4,
                prefix: ((1<<20)-1) ^ (1<<2),
            },
            EncodingSchemeForm {
                prefix_length: 18,
                bit_count: 8,
                prefix: ((1<<18)-1) ^ (1<<3),
            }
        ];

        assert_eq!(EncodingScheme::new(encoding.clone()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::new(encoding.clone()).into_iter().collect::<Vec<_>>(), forms);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_iter().collect::<Vec<_>>(), forms);
    }
}
<commit_msg>Document the encoding_scheme module.<commit_after>//! Contains structures used for announcing and decoding the Encoding
//! Scheme, as defined in https://github.com/cjdelisle/cjdns/blob/cjdns-v18/doc/Whitepaper.md#encoding-schemes
//!
//! EncodingScheme can be constructed either from bytes (ie. from the network)
//! or from an interator on `EncodingSchemeForm`.
//! Its content can be accessed either by writing it to bytes (ie. to the
//! network) or by turning it `into_iter`ator of `EncodingSchemeForm`.

use std::iter::FromIterator;

/// An item of the Encoding Scheme.
/// See https://github.com/cjdelisle/cjdns/blob/cjdns-v18/doc/Whitepaper.md#definitions
/// for its definition.
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct EncodingSchemeForm {
    pub prefix: u64,
    pub bit_count: u8,
    pub prefix_length: u8,
}

/// A list of `EncodingSchemeForm`. Can be serialized to/deserialized from
/// bytes, and constructed from/read to an iterator of `EncodingSchemeForm`.
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct EncodingScheme {
    bytes: Vec<u8>,
}

impl EncodingScheme {
    pub fn new(bytes: Vec<u8>) -> EncodingScheme {
        EncodingScheme { bytes: bytes }
    }

    pub fn bytes(&self) -> &Vec<u8> {
        &self.bytes
    }

    pub fn into_bytes(self) -> Vec<u8> {
        self.bytes
    }
}

impl IntoIterator for EncodingScheme {
    type Item = EncodingSchemeForm;
    type IntoIter = EncodingSchemeIterator;

    fn into_iter(self) -> EncodingSchemeIterator {
        EncodingSchemeIterator { bytes: self.bytes, window: 0, bits_in_window: 0, bytes_offset: 0 }
    }
}

/// Iterator of `EncodingSchemeForm`, constructed from an instance of
/// `EncodingScheme`
#[derive(Debug)]
pub struct EncodingSchemeIterator {
    bytes: Vec<u8>,
    window: u64, // integer view of a slice of the buffer (in little endian)
    bits_in_window: u8,
    bytes_offset: usize,
}

impl Iterator for EncodingSchemeIterator {
    type Item = EncodingSchemeForm;

    fn next(&mut self) -> Option<EncodingSchemeForm> {
        // Load new bytes in the window, so it is larger than any possible form
        while self.bits_in_window < 5+5+0b11111 {
            let byte = *self.bytes.get(self.bytes_offset).unwrap_or(&0);
            self.window = self.window + ((byte as u64) << self.bits_in_window);
            self.bytes_offset += 1;
            self.bits_in_window += 8;
        }

        if self.window == 0 {
            None
        }
        else {
            assert!(self.bytes_offset <= self.bytes.len()+(5+5+0b11111)/8);
            let prefix_length = self.window as u8 & 0b11111;
            self.window >>= 5;
            self.bits_in_window -= 5;

            let bit_count = self.window as u8 & 0b11111;
            self.window >>= 5;
            self.bits_in_window -= 5;

            let prefix_mask = (0b1 << prefix_length) -1; // mask for the prefix_length last digits
            let prefix = self.window & prefix_mask;
            self.window >>= prefix_length;
            self.bits_in_window -= prefix_length;

            Some(EncodingSchemeForm { prefix: prefix, bit_count: bit_count, prefix_length: prefix_length })
        }
    }
}

impl<'a> FromIterator<&'a EncodingSchemeForm> for EncodingScheme {
    fn from_iter<T: IntoIterator<Item=&'a EncodingSchemeForm>>(forms: T) -> Self {
        let mut bytes = Vec::<u8>::new();
        let mut window = 0u64;
        let mut bits_in_window = 0u8;
        println!("---");
        for form in forms {
            assert!(form.prefix_length <= 0b11111);
            assert!(form.bit_count <= 0b11111);
            assert!(form.prefix < (1 << form.prefix_length));

            window += (form.prefix as u64) << (5+5+bits_in_window);
            window += (form.bit_count as u64) << (5+bits_in_window);
            window += (form.prefix_length as u64) << (bits_in_window);

            bits_in_window += 5+5+form.prefix_length;

            while bits_in_window >= 8 {
                bytes.push((window & 0b11111111) as u8);
                window >>= 8;
                bits_in_window -= 8;
            }
        }

        if bits_in_window > 0 {
            assert!(bits_in_window < 8);
            assert!(window <= 0b11111111);
            bytes.push(window as u8);
        }

        EncodingScheme::new(bytes)
    }
}

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

    #[test]
    fn test_trivial() {
        let encoding = vec![0b011_00000, 0b00];
        let forms = vec![EncodingSchemeForm { prefix: 0, bit_count: 3, prefix_length: 0 }];

        assert_eq!(EncodingScheme::new(encoding.clone()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::new(encoding.clone()).into_iter().collect::<Vec<_>>(), forms);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_iter().collect::<Vec<_>>(), forms);
    }

    /// From https://github.com/cjdelisle/cjdns/blob/cjdns-v18/switch/test/EncodingScheme_test.c#L121-L158
    #[test]
    fn test_three() {
        let encoding = b"\x4f\xf4\xff\x29\xd9\xff\x7f\x89\xee\xff\x07".to_vec();
        let forms = vec![
            EncodingSchemeForm {
                prefix_length: 15,
                bit_count: 2,
                prefix: ((1<<15)-1) ^ (1<<1),
            },
            EncodingSchemeForm {
                prefix_length: 20,
                bit_count: 4,
                prefix: ((1<<20)-1) ^ (1<<2),
            },
            EncodingSchemeForm {
                prefix_length: 18,
                bit_count: 8,
                prefix: ((1<<18)-1) ^ (1<<3),
            }
        ];

        assert_eq!(EncodingScheme::new(encoding.clone()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_bytes(), encoding);
        assert_eq!(EncodingScheme::new(encoding.clone()).into_iter().collect::<Vec<_>>(), forms);
        assert_eq!(EncodingScheme::from_iter(forms.iter()).into_iter().collect::<Vec<_>>(), forms);
    }
}
<|endoftext|>


<commit_before>extern crate twig;

use twig::node::{ Expr, ExprValue, ExprConstant };
use twig::Environment;
use twig::Lexer;
use twig::node::Module;
use twig::parser::{ Context, Parse };

#[test]
fn test_string_expression() {
    for (template, expected) in get_tests_for_string() {
        let env = Environment::default().init_all();
        let lexer = Lexer::default(&env.lexing);
        let mut tokens = lexer.tokens(&template);
        let mut context = Context::new(&env.parsing, &mut tokens);
        let module = match Module::parse(&mut context) {
            Ok(m) => m,
            Err(e) => panic!("parsing error: {:?}", e),
        };
        assert_eq!(module.body.expect_print(), &expected);
    }
}

fn get_tests_for_string<'r>() -> Vec<(&'static str, Expr<'r>)> {
    vec![
        (r#"{{ "foo" }}"#, Expr::new_str_constant("foo", 1)),
        (r#"{{ "foo #{bar}" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_str_constant("foo ", 1)),
            right: Box::new(Expr::new_name("bar", 1)),
        }, 1)),
    ]
}
<commit_msg>Pass multi concat test.<commit_after>extern crate twig;

use twig::node::{ Expr, ExprValue, ExprConstant };
use twig::Environment;
use twig::Lexer;
use twig::node::Module;
use twig::parser::{ Context, Parse };

#[test]
fn test_string_expression() {
    for (template, expected) in get_tests_for_string() {
        let env = Environment::default().init_all();
        let lexer = Lexer::default(&env.lexing);
        let mut tokens = lexer.tokens(&template);
        let mut context = Context::new(&env.parsing, &mut tokens);
        let module = match Module::parse(&mut context) {
            Ok(m) => m,
            Err(e) => panic!("parsing error: {:?}", e),
        };
        assert_eq!(module.body.expect_print(), &expected);
    }
}

fn get_tests_for_string<'r>() -> Vec<(&'static str, Expr<'r>)> {
    vec![
        (r#"{{ "foo" }}"#, Expr::new_str_constant("foo", 1)),
        (r#"{{ "foo #{bar}" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_str_constant("foo ", 1)),
            right: Box::new(Expr::new_name("bar", 1)),
        }, 1)),
        (r#"{{ "foo #{bar} baz" }}"#, Expr::new_at(ExprValue::Concat {
            left: Box::new(Expr::new_at(ExprValue::Concat {
                left: Box::new(Expr::new_str_constant("foo ", 1)),
                right: Box::new(Expr::new_name("bar", 1)),
            }, 1)),
            right: Box::new(Expr::new_str_constant(" baz", 1)),
        }, 1)),
    ]
}
<|endoftext|>