<commit_before>use super::{
    ast::{self, Assignment, Ast, AstKind, Identifier, Int, Struct, Symbol, Text},
    cst::{self, CstDb},
    cst_to_ast::CstToAst,
    error::{CompilerError, CompilerErrorPayload},
    hir::{self, Body, Expression, HirError, Lambda},
};
use crate::{builtin_functions, input::Input};
use im::HashMap;
use std::{ops::Range, sync::Arc};

#[salsa::query_group(AstToHirStorage)]
pub trait AstToHir: CstDb + CstToAst {
    fn hir_to_ast_id(&self, id: hir::Id) -> Option<ast::Id>;
    fn hir_to_cst_id(&self, id: hir::Id) -> Option<cst::Id>;
    fn hir_id_to_span(&self, id: hir::Id) -> Option<Range<usize>>;
    fn hir_id_to_display_span(&self, id: hir::Id) -> Option<Range<usize>>;

    fn ast_to_hir_id(&self, id: ast::Id) -> Option<hir::Id>;
    fn cst_to_hir_id(&self, input: Input, id: cst::Id) -> Option<hir::Id>;

    fn hir(&self, input: Input) -> Option<(Arc<Body>, HashMap<hir::Id, ast::Id>)>;
}

fn hir_to_ast_id(db: &dyn AstToHir, id: hir::Id) -> Option<ast::Id> {
    let (_, hir_to_ast_id_mapping) = db.hir(id.input.clone()).unwrap();
    hir_to_ast_id_mapping.get(&id).cloned()
}
fn hir_to_cst_id(db: &dyn AstToHir, id: hir::Id) -> Option<cst::Id> {
    db.ast_to_cst_id(db.hir_to_ast_id(id.clone())?)
}
fn hir_id_to_span(db: &dyn AstToHir, id: hir::Id) -> Option<Range<usize>> {
    db.ast_id_to_span(db.hir_to_ast_id(id.clone())?)
}
fn hir_id_to_display_span(db: &dyn AstToHir, id: hir::Id) -> Option<Range<usize>> {
    let cst_id = db.hir_to_cst_id(id.clone())?;
    Some(db.find_cst(id.input, cst_id).display_span())
}

fn ast_to_hir_id(db: &dyn AstToHir, id: ast::Id) -> Option<hir::Id> {
    let (_, hir_to_ast_id_mapping) = db.hir(id.input.clone()).unwrap();
    hir_to_ast_id_mapping
        .iter()
        .find_map(|(key, value)| if value == &id { Some(key) } else { None })
        .cloned()
}
fn cst_to_hir_id(db: &dyn AstToHir, input: Input, id: cst::Id) -> Option<hir::Id> {
    let id = db.cst_to_ast_id(input, id)?;
    db.ast_to_hir_id(id)
}

fn hir(db: &dyn AstToHir, input: Input) -> Option<(Arc<Body>, HashMap<hir::Id, ast::Id>)> {
    let (ast, _) = db.ast(input.clone())?;

    let mut context = Context {
        db,
        input: input.clone(),
    };
    let mut compiler = Compiler::new(&mut context);
    compiler.compile(&ast);
    Some((Arc::new(compiler.body), compiler.id_mapping))
}

struct Context<'c> {
    db: &'c dyn AstToHir,
    input: Input,
}

struct Compiler<'c> {
    context: &'c Context<'c>,
    id_mapping: HashMap<hir::Id, ast::Id>,
    body: Body,
    parent_keys: Vec<String>,
    identifiers: HashMap<String, hir::Id>,
}
impl<'c> Compiler<'c> {
    fn new(context: &'c Context<'c>) -> Self {
        let builtin_identifiers = builtin_functions::VALUES
            .iter()
            .map(|builtin_function| {
                let string = format!("builtin{:?}", builtin_function);
                (
                    string.clone(),
                    hir::Id::new(context.input.clone(), vec![string]),
                )
            })
            .collect::<HashMap<_, _>>();

        let mut compiler = Compiler {
            context,
            id_mapping: HashMap::new(),
            parent_keys: vec![],
            body: Body::new(),
            identifiers: builtin_identifiers,
        };
        compiler.generate_use();
        compiler
    }

    fn generate_use(&mut self) {
        let mut assignment_inner = Compiler::<'c> {
            context: &mut self.context,
            id_mapping: self.id_mapping.clone(),
            body: Body::new(),
            parent_keys: add_keys(&self.parent_keys, "use".to_string()),
            identifiers: self.identifiers.clone(),
        };

        let lambda_keys = add_keys(&assignment_inner.parent_keys, "0".to_string());
        let lambda_parameter_id = hir::Id::new(
            assignment_inner.context.input.clone(),
            add_keys(&lambda_keys[..], "target".to_string()),
        );
        let mut lambda_inner = Compiler::<'c> {
            context: &mut assignment_inner.context,
            id_mapping: assignment_inner.id_mapping.clone(),
            body: Body::new(),
            parent_keys: lambda_keys.clone(),
            identifiers: assignment_inner.identifiers.clone(),
        };

        let panic_id = lambda_inner.identifiers["builtinPanic"].clone();
        match &lambda_inner.context.input {
            Input::File(path) => {
                let current_path_content = path
                    .iter()
                    .enumerate()
                    .map(|(index, it)| {
                        (
                            lambda_inner
                                .push_without_ast_mapping(Expression::Int(index as u64), None),
                            lambda_inner
                                .push_without_ast_mapping(Expression::Text(it.to_owned()), None),
                        )
                    })
                    .collect();
                let current_path = lambda_inner
                    .push_without_ast_mapping(Expression::Struct(current_path_content), None);
                lambda_inner.push_without_ast_mapping(
                    Expression::Call {
                        function: lambda_inner.identifiers["builtinUse"].clone(),
                        arguments: vec![current_path, lambda_parameter_id.clone()],
                    },
                    None,
                );
            }
            Input::ExternalFile(_) => {
                let message_id = lambda_inner.push_without_ast_mapping(
                    Expression::Text(
                        "File doesn't belong to the currently opened project.".to_string(),
                    ),
                    None,
                );
                lambda_inner.push_without_ast_mapping(
                    Expression::Call {
                        function: panic_id,
                        arguments: vec![message_id],
                    },
                    None,
                );
            }
            Input::Untitled(_) => {
                let message_id = lambda_inner.push_without_ast_mapping(
                    Expression::Text("Untitled files can't call `use`.".to_string()),
                    None,
                );
                lambda_inner.push_without_ast_mapping(
                    Expression::Call {
                        function: panic_id,
                        arguments: vec![message_id],
                    },
                    None,
                );
            }
        }

        assignment_inner.id_mapping = lambda_inner.id_mapping;
        assignment_inner.push_without_ast_mapping(
            Expression::Lambda(Lambda {
                parameters: vec![hir::Id::new(
                    self.context.input.clone(),
                    vec!["use".to_string(), "0".to_string(), "target".to_string()],
                )],
                body: lambda_inner.body,
            }),
            None,
        );

        self.id_mapping = assignment_inner.id_mapping;

        self.push_without_ast_mapping(
            Expression::Body(assignment_inner.body),
            Some("use".to_string()),
        );
    }

    fn compile(&mut self, asts: &[Ast]) {
        if asts.is_empty() {
            self.push_without_ast_mapping(Expression::nothing(), None);
        } else {
            for ast in asts.into_iter() {
                self.compile_single(ast);
            }
        }
    }
    fn compile_single(&mut self, ast: &Ast) -> hir::Id {
        match &ast.kind {
            AstKind::Int(Int(int)) => {
                self.push(ast.id.clone(), Expression::Int(int.to_owned()), None)
            }
            AstKind::Text(Text(string)) => self.push(
                ast.id.clone(),
                Expression::Text(string.value.to_owned()),
                None,
            ),
            AstKind::Identifier(Identifier(symbol)) => {
                let reference = match self.identifiers.get(&symbol.value) {
                    Some(reference) => reference.to_owned(),
                    None => {
                        return self.push(
                            symbol.id.clone(),
                            Expression::Error {
                                child: None,
                                errors: vec![CompilerError {
                                    input: ast.id.input.clone(),
                                    span: self.context.db.ast_id_to_span(ast.id.clone()).unwrap(),
                                    payload: CompilerErrorPayload::Hir(
                                        HirError::UnknownReference {
                                            symbol: symbol.value.clone(),
                                        },
                                    ),
                                }],
                            },
                            None,
                        );
                    }
                };
                self.push(
                    ast.id.clone(),
                    Expression::Reference(reference.to_owned()),
                    None,
                )
            }
            AstKind::Symbol(Symbol(symbol)) => self.push(
                ast.id.clone(),
                Expression::Symbol(symbol.value.to_owned()),
                None,
            ),
            AstKind::Struct(Struct { fields }) => {
                let fields = fields
                    .iter()
                    .map(|(key, value)| (self.compile_single(key), self.compile_single(value)))
                    .collect();
                self.push(ast.id.clone(), Expression::Struct(fields), None)
            }
            AstKind::Lambda(ast::Lambda {
                parameters,
                body: body_asts,
            }) => {
                let mut body = Body::new();
                let lambda_id = self.create_next_id(ast.id.clone(), None);
                let mut identifiers = self.identifiers.clone();

                for parameter in parameters.iter() {
                    let name = parameter.value.to_string();
                    let id = hir::Id::new(
                        self.context.input.clone(),
                        add_keys(&lambda_id.keys, name.clone()),
                    );
                    self.id_mapping.insert(id.clone(), parameter.id.clone());
                    body.identifiers.insert(id.clone(), name.clone());
                    identifiers.insert(name, id);
                }
                let mut inner = Compiler::<'c> {
                    context: &mut self.context,
                    id_mapping: self.id_mapping.clone(),
                    body,
                    parent_keys: lambda_id.keys.clone(),
                    identifiers,
                };

                inner.compile(&body_asts);
                self.id_mapping = inner.id_mapping;
                self.push_with_existing_id(
                    lambda_id.clone(),
                    Expression::Lambda(Lambda {
                        parameters: parameters
                            .iter()
                            .map(|parameter| {
                                hir::Id::new(
                                    self.context.input.clone(),
                                    add_keys(&lambda_id.keys[..], parameter.value.to_string()),
                                )
                            })
                            .collect(),
                        body: inner.body,
                    }),
                    None,
                )
            }
            AstKind::Call(ast::Call { name, arguments }) => {
                let arguments = arguments
                    .iter()
                    .map(|argument| self.compile_single(argument))
                    .collect();

                let function = match self.identifiers.get(&name.value) {
                    Some(function) => function.to_owned(),
                    None => {
                        return self.push(
                            name.id.clone(),
                            Expression::Error {
                                child: None,
                                errors: vec![CompilerError {
                                    input: name.id.input.clone(),
                                    span: self.context.db.ast_id_to_span(name.id.clone()).unwrap(),
                                    payload: CompilerErrorPayload::Hir(HirError::UnknownFunction {
                                        name: name.value.clone(),
                                    }),
                                }],
                            },
                            None,
                        );
                    }
                };
                self.push(
                    ast.id.clone(),
                    Expression::Call {
                        function,
                        arguments,
                    },
                    None,
                )
            }
            AstKind::Assignment(Assignment { name, body }) => {
                let name = name.value.to_owned();
                let mut inner = Compiler::<'c> {
                    context: &mut self.context,
                    id_mapping: self.id_mapping.clone(),
                    body: Body::new(),
                    parent_keys: add_keys(&self.parent_keys, name.clone()),
                    identifiers: self.identifiers.clone(),
                };
                inner.compile(&body);
                self.id_mapping = inner.id_mapping;
                self.push(ast.id.clone(), Expression::Body(inner.body), Some(name))
            }
            AstKind::Error { child, errors } => {
                let child = if let Some(child) = child {
                    Some(self.compile_single(&*child))
                } else {
                    None
                };
                self.push(
                    ast.id.clone(),
                    Expression::Error {
                        child,
                        errors: errors.clone(),
                    },
                    None,
                )
            }
        }
    }

    fn push(
        &mut self,
        ast_id: ast::Id,
        expression: Expression,
        identifier: Option<String>,
    ) -> hir::Id {
        let id = self.create_next_id(ast_id, identifier.clone());
        self.push_with_existing_id(id, expression, identifier)
    }
    fn push_without_ast_mapping(
        &mut self,
        expression: Expression,
        identifier: Option<String>,
    ) -> hir::Id {
        let id = self.create_next_id_without_ast_mapping(identifier.clone());
        self.push_with_existing_id(id, expression, identifier)
    }
    fn push_with_existing_id(
        &mut self,
        id: hir::Id,
        expression: Expression,
        identifier: Option<String>,
    ) -> hir::Id {
        self.body.push(id.to_owned(), expression, None);
        if let Some(identifier) = identifier {
            self.identifiers.insert(identifier, id.clone());
        }
        id
    }

    fn create_next_id(&mut self, ast_id: ast::Id, key: Option<String>) -> hir::Id {
        let id = self.create_next_id_without_ast_mapping(key);
        assert!(self.id_mapping.insert(id.to_owned(), ast_id).is_none());
        id
    }
    fn create_next_id_without_ast_mapping(&mut self, key: Option<String>) -> hir::Id {
        for disambiguator in 0.. {
            let last_part = if let Some(key) = &key {
                let disambiguator = if disambiguator == 0 {
                    "".to_string()
                } else {
                    format!("${}", disambiguator - 1)
                };
                format!("{}{}", key, disambiguator)
            } else {
                format!("{}", disambiguator)
            };
            let id = hir::Id::new(
                self.context.input.clone(),
                add_keys(&self.parent_keys, last_part),
            );
            if !self.id_mapping.contains_key(&id) {
                return id;
            }
        }
        unreachable!()
    }
}

fn add_keys(parents: &[String], id: String) -> Vec<String> {
    parents
        .iter()
        .map(|it| it.to_string())
        .chain(vec![id])
        .collect()
}
<commit_msg>Fix generation of IDs without identifiers<commit_after>use super::{
    ast::{self, Assignment, Ast, AstKind, Identifier, Int, Struct, Symbol, Text},
    cst::{self, CstDb},
    cst_to_ast::CstToAst,
    error::{CompilerError, CompilerErrorPayload},
    hir::{self, Body, Expression, HirError, Lambda},
};
use crate::{builtin_functions, input::Input};
use im::HashMap;
use std::{ops::Range, sync::Arc};

#[salsa::query_group(AstToHirStorage)]
pub trait AstToHir: CstDb + CstToAst {
    fn hir_to_ast_id(&self, id: hir::Id) -> Option<ast::Id>;
    fn hir_to_cst_id(&self, id: hir::Id) -> Option<cst::Id>;
    fn hir_id_to_span(&self, id: hir::Id) -> Option<Range<usize>>;
    fn hir_id_to_display_span(&self, id: hir::Id) -> Option<Range<usize>>;

    fn ast_to_hir_id(&self, id: ast::Id) -> Option<hir::Id>;
    fn cst_to_hir_id(&self, input: Input, id: cst::Id) -> Option<hir::Id>;

    fn hir(&self, input: Input) -> Option<(Arc<Body>, HashMap<hir::Id, ast::Id>)>;
}

fn hir_to_ast_id(db: &dyn AstToHir, id: hir::Id) -> Option<ast::Id> {
    let (_, hir_to_ast_id_mapping) = db.hir(id.input.clone()).unwrap();
    hir_to_ast_id_mapping.get(&id).cloned()
}
fn hir_to_cst_id(db: &dyn AstToHir, id: hir::Id) -> Option<cst::Id> {
    db.ast_to_cst_id(db.hir_to_ast_id(id.clone())?)
}
fn hir_id_to_span(db: &dyn AstToHir, id: hir::Id) -> Option<Range<usize>> {
    db.ast_id_to_span(db.hir_to_ast_id(id.clone())?)
}
fn hir_id_to_display_span(db: &dyn AstToHir, id: hir::Id) -> Option<Range<usize>> {
    let cst_id = db.hir_to_cst_id(id.clone())?;
    Some(db.find_cst(id.input, cst_id).display_span())
}

fn ast_to_hir_id(db: &dyn AstToHir, id: ast::Id) -> Option<hir::Id> {
    let (_, hir_to_ast_id_mapping) = db.hir(id.input.clone()).unwrap();
    hir_to_ast_id_mapping
        .iter()
        .find_map(|(key, value)| if value == &id { Some(key) } else { None })
        .cloned()
}
fn cst_to_hir_id(db: &dyn AstToHir, input: Input, id: cst::Id) -> Option<hir::Id> {
    let id = db.cst_to_ast_id(input, id)?;
    db.ast_to_hir_id(id)
}

fn hir(db: &dyn AstToHir, input: Input) -> Option<(Arc<Body>, HashMap<hir::Id, ast::Id>)> {
    let (ast, _) = db.ast(input.clone())?;

    let mut context = Context {
        db,
        input: input.clone(),
    };
    let mut compiler = Compiler::new(&mut context);
    compiler.compile(&ast);
    let id_mapping_of_existing_ids = compiler
        .id_mapping
        .into_iter()
        .filter_map(|(key, value)| {
            if let Some(value) = value {
                Some((key, value))
            } else {
                None
            }
        })
        .collect();
    Some((Arc::new(compiler.body), id_mapping_of_existing_ids))
}

struct Context<'c> {
    db: &'c dyn AstToHir,
    input: Input,
}

struct Compiler<'c> {
    context: &'c Context<'c>,
    id_mapping: HashMap<hir::Id, Option<ast::Id>>,
    body: Body,
    parent_keys: Vec<String>,
    identifiers: HashMap<String, hir::Id>,
}
impl<'c> Compiler<'c> {
    fn new(context: &'c Context<'c>) -> Self {
        let builtin_identifiers = builtin_functions::VALUES
            .iter()
            .map(|builtin_function| {
                let string = format!("builtin{:?}", builtin_function);
                (
                    string.clone(),
                    hir::Id::new(context.input.clone(), vec![string]),
                )
            })
            .collect::<HashMap<_, _>>();

        let mut compiler = Compiler {
            context,
            id_mapping: HashMap::new(),
            parent_keys: vec![],
            body: Body::new(),
            identifiers: builtin_identifiers,
        };
        compiler.generate_use();
        compiler
    }

    fn generate_use(&mut self) {
        let mut assignment_inner = Compiler::<'c> {
            context: &mut self.context,
            id_mapping: self.id_mapping.clone(),
            body: Body::new(),
            parent_keys: add_keys(&self.parent_keys, "use".to_string()),
            identifiers: self.identifiers.clone(),
        };

        let lambda_keys = add_keys(&assignment_inner.parent_keys, "0".to_string());
        let lambda_parameter_id = hir::Id::new(
            assignment_inner.context.input.clone(),
            add_keys(&lambda_keys[..], "target".to_string()),
        );
        let mut lambda_inner = Compiler::<'c> {
            context: &mut assignment_inner.context,
            id_mapping: assignment_inner.id_mapping.clone(),
            body: Body::new(),
            parent_keys: lambda_keys.clone(),
            identifiers: assignment_inner.identifiers.clone(),
        };

        let panic_id = lambda_inner.identifiers["builtinPanic"].clone();
        match &lambda_inner.context.input {
            Input::File(path) => {
                let current_path_content = path
                    .iter()
                    .enumerate()
                    .map(|(index, it)| {
                        (
                            lambda_inner.push(
                                None,
                                Expression::Int(index as u64),
                                // Some("key".to_string()),
                                None,
                            ),
                            lambda_inner.push(
                                None,
                                Expression::Text(it.to_owned()),
                                // Some("raw_path".to_string()),
                                None,
                            ),
                        )
                    })
                    .collect();
                let current_path = lambda_inner.push(
                    None,
                    Expression::Struct(current_path_content),
                    Some("path".to_string()),
                );
                lambda_inner.push(
                    None,
                    Expression::Call {
                        function: lambda_inner.identifiers["builtinUse"].clone(),
                        arguments: vec![current_path, lambda_parameter_id.clone()],
                    },
                    None,
                );
            }
            Input::ExternalFile(_) => {
                let message_id = lambda_inner.push(
                    None,
                    Expression::Text(
                        "File doesn't belong to the currently opened project.".to_string(),
                    ),
                    None,
                );
                lambda_inner.push(
                    None,
                    Expression::Call {
                        function: panic_id,
                        arguments: vec![message_id],
                    },
                    None,
                );
            }
            Input::Untitled(_) => {
                let message_id = lambda_inner.push(
                    None,
                    Expression::Text("Untitled files can't call `use`.".to_string()),
                    None,
                );
                lambda_inner.push(
                    None,
                    Expression::Call {
                        function: panic_id,
                        arguments: vec![message_id],
                    },
                    None,
                );
            }
        }

        assignment_inner.id_mapping = lambda_inner.id_mapping;
        assignment_inner.push(
            None,
            Expression::Lambda(Lambda {
                parameters: vec![hir::Id::new(
                    self.context.input.clone(),
                    vec!["use".to_string(), "0".to_string(), "target".to_string()],
                )],
                body: lambda_inner.body,
            }),
            None,
        );

        self.id_mapping = assignment_inner.id_mapping;

        self.push(
            None,
            Expression::Body(assignment_inner.body),
            Some("use".to_string()),
        );
    }

    fn compile(&mut self, asts: &[Ast]) {
        if asts.is_empty() {
            self.push(None, Expression::nothing(), None);
        } else {
            for ast in asts.into_iter() {
                self.compile_single(ast);
            }
        }
    }
    fn compile_single(&mut self, ast: &Ast) -> hir::Id {
        match &ast.kind {
            AstKind::Int(Int(int)) => {
                self.push(Some(ast.id.clone()), Expression::Int(int.to_owned()), None)
            }
            AstKind::Text(Text(string)) => self.push(
                Some(ast.id.clone()),
                Expression::Text(string.value.to_owned()),
                None,
            ),
            AstKind::Identifier(Identifier(symbol)) => {
                let reference = match self.identifiers.get(&symbol.value) {
                    Some(reference) => reference.to_owned(),
                    None => {
                        return self.push(
                            Some(symbol.id.clone()),
                            Expression::Error {
                                child: None,
                                errors: vec![CompilerError {
                                    input: ast.id.input.clone(),
                                    span: self.context.db.ast_id_to_span(ast.id.clone()).unwrap(),
                                    payload: CompilerErrorPayload::Hir(
                                        HirError::UnknownReference {
                                            symbol: symbol.value.clone(),
                                        },
                                    ),
                                }],
                            },
                            None,
                        );
                    }
                };
                self.push(
                    Some(ast.id.clone()),
                    Expression::Reference(reference.to_owned()),
                    None,
                )
            }
            AstKind::Symbol(Symbol(symbol)) => self.push(
                Some(ast.id.clone()),
                Expression::Symbol(symbol.value.to_owned()),
                None,
            ),
            AstKind::Struct(Struct { fields }) => {
                let fields = fields
                    .iter()
                    .map(|(key, value)| (self.compile_single(key), self.compile_single(value)))
                    .collect();
                self.push(Some(ast.id.clone()), Expression::Struct(fields), None)
            }
            AstKind::Lambda(ast::Lambda {
                parameters,
                body: body_asts,
            }) => {
                let mut body = Body::new();
                let lambda_id = self.create_next_id(Some(ast.id.clone()), None);
                let mut identifiers = self.identifiers.clone();

                for parameter in parameters.iter() {
                    let name = parameter.value.to_string();
                    let id = hir::Id::new(
                        self.context.input.clone(),
                        add_keys(&lambda_id.keys, name.clone()),
                    );
                    self.id_mapping
                        .insert(id.clone(), Some(parameter.id.clone()));
                    body.identifiers.insert(id.clone(), name.clone());
                    identifiers.insert(name, id);
                }
                let mut inner = Compiler::<'c> {
                    context: &mut self.context,
                    id_mapping: self.id_mapping.clone(),
                    body,
                    parent_keys: lambda_id.keys.clone(),
                    identifiers,
                };

                inner.compile(&body_asts);
                self.id_mapping = inner.id_mapping;
                self.push_with_existing_id(
                    lambda_id.clone(),
                    Expression::Lambda(Lambda {
                        parameters: parameters
                            .iter()
                            .map(|parameter| {
                                hir::Id::new(
                                    self.context.input.clone(),
                                    add_keys(&lambda_id.keys[..], parameter.value.to_string()),
                                )
                            })
                            .collect(),
                        body: inner.body,
                    }),
                    None,
                )
            }
            AstKind::Call(ast::Call { name, arguments }) => {
                let arguments = arguments
                    .iter()
                    .map(|argument| self.compile_single(argument))
                    .collect();

                let function = match self.identifiers.get(&name.value) {
                    Some(function) => function.to_owned(),
                    None => {
                        return self.push(
                            Some(name.id.clone()),
                            Expression::Error {
                                child: None,
                                errors: vec![CompilerError {
                                    input: name.id.input.clone(),
                                    span: self.context.db.ast_id_to_span(name.id.clone()).unwrap(),
                                    payload: CompilerErrorPayload::Hir(HirError::UnknownFunction {
                                        name: name.value.clone(),
                                    }),
                                }],
                            },
                            None,
                        );
                    }
                };
                self.push(
                    Some(ast.id.clone()),
                    Expression::Call {
                        function,
                        arguments,
                    },
                    None,
                )
            }
            AstKind::Assignment(Assignment { name, body }) => {
                let name = name.value.to_owned();
                let mut inner = Compiler::<'c> {
                    context: &mut self.context,
                    id_mapping: self.id_mapping.clone(),
                    body: Body::new(),
                    parent_keys: add_keys(&self.parent_keys, name.clone()),
                    identifiers: self.identifiers.clone(),
                };
                inner.compile(&body);
                self.id_mapping = inner.id_mapping;
                self.push(
                    Some(ast.id.clone()),
                    Expression::Body(inner.body),
                    Some(name),
                )
            }
            AstKind::Error { child, errors } => {
                let child = if let Some(child) = child {
                    Some(self.compile_single(&*child))
                } else {
                    None
                };
                self.push(
                    Some(ast.id.clone()),
                    Expression::Error {
                        child,
                        errors: errors.clone(),
                    },
                    None,
                )
            }
        }
    }

    fn push(
        &mut self,
        ast_id: Option<ast::Id>,
        expression: Expression,
        identifier: Option<String>,
    ) -> hir::Id {
        let id = self.create_next_id(ast_id, identifier.clone());
        self.push_with_existing_id(id, expression, identifier)
    }
    fn push_with_existing_id(
        &mut self,
        id: hir::Id,
        expression: Expression,
        identifier: Option<String>,
    ) -> hir::Id {
        self.body
            .push(id.to_owned(), expression, identifier.clone());
        if let Some(identifier) = identifier {
            self.identifiers.insert(identifier, id.clone());
        }
        id
    }

    fn create_next_id(&mut self, ast_id: Option<ast::Id>, key: Option<String>) -> hir::Id {
        for disambiguator in 0.. {
            let last_part = if let Some(key) = &key {
                if disambiguator == 0 {
                    key.to_string()
                } else {
                    format!("{}${}", key, disambiguator - 1)
                }
            } else {
                format!("{}", disambiguator)
            };
            let id = hir::Id::new(
                self.context.input.clone(),
                add_keys(&self.parent_keys, last_part),
            );
            if !self.id_mapping.contains_key(&id) {
                self.id_mapping.insert(id.to_owned(), ast_id).is_none();
                return id;
            }
        }
        unreachable!()
    }
}

fn add_keys(parents: &[String], id: String) -> Vec<String> {
    parents
        .iter()
        .map(|it| it.to_string())
        .chain(vec![id])
        .collect()
}
<|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/. */

use api::{BorderRadius, ComplexClipRegion, DeviceIntRect, ImageMask, ImageRendering, LayerPoint};
use api::{ClipMode, LayerRect, LayerSize};
use api::{LayerToWorldTransform, LayoutPoint, LayoutVector2D, LocalClip};
use border::BorderCornerClipSource;
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use prim_store::{ClipData, ImageMaskData};
use resource_cache::ResourceCache;
use util::{extract_inner_rect_safe, TransformedRect};

pub const MAX_CLIP: f32 = 1000000.0;

pub type ClipStore = FreeList<ClipSources>;
pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
pub type ClipSourcesWeakHandle = WeakFreeListHandle<ClipSources>;

#[derive(Clone, Debug)]
pub struct ClipRegion {
    pub main: LayerRect,
    pub image_mask: Option<ImageMask>,
    pub complex_clips: Vec<ComplexClipRegion>,
}

impl ClipRegion {
    pub fn create_for_clip_node(
        rect: LayerRect,
        mut complex_clips: Vec<ComplexClipRegion>,
        mut image_mask: Option<ImageMask>,
        reference_frame_relative_offset: &LayoutVector2D,
    ) -> ClipRegion {
        let rect = rect.translate(reference_frame_relative_offset);

        if let Some(ref mut image_mask) = image_mask {
            image_mask.rect = image_mask.rect.translate(reference_frame_relative_offset);
        }

        for complex_clip in complex_clips.iter_mut() {
            complex_clip.rect = complex_clip.rect.translate(reference_frame_relative_offset);
        }

        ClipRegion {
            main: rect,
            image_mask,
            complex_clips,
        }
    }

    pub fn create_for_clip_node_with_local_clip(
        local_clip: &LocalClip,
        reference_frame_relative_offset: &LayoutVector2D
    ) -> ClipRegion {
        let complex_clips = match local_clip {
            &LocalClip::Rect(_) => Vec::new(),
            &LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
        };
        ClipRegion::create_for_clip_node(
            *local_clip.clip_rect(),
            complex_clips,
            None,
            reference_frame_relative_offset
        )
    }
}

#[derive(Debug)]
pub enum ClipSource {
    Rectangle(LayerRect),
    RoundedRectangle(LayerRect, BorderRadius, ClipMode),
    Image(ImageMask),
    /// TODO(gw): This currently only handles dashed style
    /// clips, where the border style is dashed for both
    /// adjacent border edges. Expand to handle dotted style
    /// and different styles per edge.
    BorderCorner(BorderCornerClipSource),
}

impl From<ClipRegion> for ClipSources {
    fn from(region: ClipRegion) -> ClipSources {
        let mut clips = Vec::new();

        if let Some(info) = region.image_mask {
            clips.push(ClipSource::Image(info));
        }

        clips.push(ClipSource::Rectangle(region.main));

        for complex in region.complex_clips {
            clips.push(ClipSource::RoundedRectangle(
                complex.rect,
                complex.radii,
                complex.mode,
            ));
        }

        ClipSources::new(clips)
    }
}

impl ClipSource {
    pub fn contains(&self, point: &LayerPoint) -> bool {
        // We currently do not handle all types of clip sources, because they
        // aren't used for ClipScrollNodes and this method is only used during hit testing.
        match self {
            &ClipSource::Rectangle(ref rectangle) => rectangle.contains(point),
            &ClipSource::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            &ClipSource::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
            _ => unreachable!("Tried to call contains on an unsupported ClipSource."),
        }
    }

}

#[derive(Debug)]
pub struct ClipSources {
    pub clips: Vec<(ClipSource, GpuCacheHandle)>,
    pub bounds: MaskBounds,
}

impl ClipSources {
    pub fn new(clips: Vec<ClipSource>) -> ClipSources {
        let clips = clips
            .into_iter()
            .map(|clip| (clip, GpuCacheHandle::new()))
            .collect();

        ClipSources {
            clips,
            bounds: MaskBounds {
                inner: None,
                outer: None,
            },
        }
    }

    pub fn clips(&self) -> &[(ClipSource, GpuCacheHandle)] {
        &self.clips
    }

    pub fn update(
        &mut self,
        layer_transform: &LayerToWorldTransform,
        gpu_cache: &mut GpuCache,
        resource_cache: &mut ResourceCache,
        device_pixel_ratio: f32,
    ) {
        if self.clips.is_empty() {
            return;
        }

        // compute the local bounds
        if self.bounds.inner.is_none() {
            let mut local_rect = Some(LayerRect::new(
                LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
                LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP),
            ));
            let mut local_inner = local_rect;
            let mut has_clip_out = false;
            let mut has_border_clip = false;

            for &(ref source, _) in &self.clips {
                match *source {
                    ClipSource::Image(ref mask) => {
                        if !mask.repeat {
                            local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
                        }
                        local_inner = None;
                    }
                    ClipSource::Rectangle(rect) => {
                        local_rect = local_rect.and_then(|r| r.intersection(&rect));
                        local_inner = local_inner.and_then(|r| r.intersection(&rect));
                    }
                    ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
                        // Once we encounter a clip-out, we just assume the worst
                        // case clip mask size, for now.
                        if mode == ClipMode::ClipOut {
                            has_clip_out = true;
                        }

                        local_rect = local_rect.and_then(|r| r.intersection(rect));

                        let inner_rect = extract_inner_rect_safe(rect, radius);
                        local_inner = local_inner
                            .and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner)));
                    }
                    ClipSource::BorderCorner { .. } => {
                        has_border_clip = true;
                    }
                }
            }

            // Work out the type of mask geometry we have, based on the
            // list of clip sources above.
            self.bounds = if has_clip_out || has_border_clip {
                // For clip-out, the mask rect is not known.
                MaskBounds {
                    outer: None,
                    inner: Some(LayerRect::zero().into()),
                }
            } else {
                MaskBounds {
                    outer: Some(local_rect.unwrap_or(LayerRect::zero()).into()),
                    inner: Some(local_inner.unwrap_or(LayerRect::zero()).into()),
                }
            };
        }

        // update the screen bounds
        self.bounds.update(layer_transform, device_pixel_ratio);

        for &mut (ref mut source, ref mut handle) in &mut self.clips {
            if let Some(mut request) = gpu_cache.request(handle) {
                match *source {
                    ClipSource::Image(ref mask) => {
                        let data = ImageMaskData {
                            local_rect: mask.rect,
                        };
                        data.write_gpu_blocks(request);
                    }
                    ClipSource::Rectangle(rect) => {
                        let data = ClipData::uniform(rect, 0.0, ClipMode::Clip);
                        data.write(&mut request);
                    }
                    ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
                        let data = ClipData::rounded_rect(rect, radius, mode);
                        data.write(&mut request);
                    }
                    ClipSource::BorderCorner(ref mut source) => {
                        source.write(request);
                    }
                }
            }
        }

        for &(ref clip, _) in &self.clips {
            if let ClipSource::Image(ref mask) = *clip {
                resource_cache.request_image(mask.image, ImageRendering::Auto, None, gpu_cache);
            }
        }
    }

    /// Whether or not this ClipSources has any clips (does any clipping).
    pub fn has_clips(&self) -> bool {
        !self.clips.is_empty()
    }
}

/// Represents a local rect and a device space
/// rectangles that are either outside or inside bounds.
#[derive(Clone, Debug, PartialEq)]
pub struct Geometry {
    pub local_rect: LayerRect,
    pub device_rect: DeviceIntRect,
}

impl From<LayerRect> for Geometry {
    fn from(local_rect: LayerRect) -> Self {
        Geometry {
            local_rect,
            device_rect: DeviceIntRect::zero(),
        }
    }
}

/// Depending on the complexity of the clip, we may either
/// know the outer and/or inner rect, or neither or these.
/// In the case of a clip-out, we currently set the mask
/// bounds to be unknown. This is conservative, but ensures
/// correctness. In the future we can make this a lot
/// more clever with some proper region handling.
#[derive(Clone, Debug, PartialEq)]
pub struct MaskBounds {
    pub outer: Option<Geometry>,
    pub inner: Option<Geometry>,
}

impl MaskBounds {
    pub fn update(&mut self, transform: &LayerToWorldTransform, device_pixel_ratio: f32) {
        if let Some(ref mut outer) = self.outer {
            let transformed =
                TransformedRect::new(&outer.local_rect, transform, device_pixel_ratio);
            outer.device_rect = transformed.bounding_rect;
        }
        if let Some(ref mut inner) = self.inner {
            let transformed =
                TransformedRect::new(&inner.local_rect, transform, device_pixel_ratio);
            inner.device_rect = transformed.inner_rect;
        }
    }
}

pub trait Contains {
    fn contains(&self, point: &LayoutPoint) -> bool;
}

impl Contains for LocalClip {
    fn contains(&self, point: &LayoutPoint) -> bool {
        if !self.clip_rect().contains(point) {
            return false;
        }
        match self {
            &LocalClip::Rect(..) => true,
            &LocalClip::RoundedRect(_, complex_clip) => complex_clip.contains(point),
        }
    }
}

impl Contains for ComplexClipRegion {
    fn contains(&self, point: &LayoutPoint) -> bool {
        rounded_rectangle_contains_point(point, &self.rect, &self.radii)
    }
}

fn rounded_rectangle_contains_point(point: &LayoutPoint,
                                    rect: &LayerRect,
                                    radii: &BorderRadius)
                                    -> bool {
    if !rect.contains(point) {
        return false;
    }

    let top_left_center = rect.origin + radii.top_left.to_vector();
    if top_left_center.x > point.x && top_left_center.y > point.y &&
       !Ellipse::new(radii.top_left).contains(*point - top_left_center.to_vector()) {
        return false;
    }

    let bottom_right_center = rect.bottom_right() - radii.bottom_right.to_vector();
    if bottom_right_center.x < point.x && bottom_right_center.y < point.y &&
       !Ellipse::new(radii.bottom_right).contains(*point - bottom_right_center.to_vector()) {
        return false;
    }

    let top_right_center = rect.top_right() +
                           LayoutVector2D::new(-radii.top_right.width, radii.top_right.height);
    if top_right_center.x < point.x && top_right_center.y > point.y &&
       !Ellipse::new(radii.top_right).contains(*point - top_right_center.to_vector()) {
        return false;
    }

    let bottom_left_center = rect.bottom_left() +
                             LayoutVector2D::new(radii.bottom_left.width, -radii.bottom_left.height);
    if bottom_left_center.x > point.x && bottom_left_center.y < point.y &&
       !Ellipse::new(radii.bottom_left).contains(*point - bottom_left_center.to_vector()) {
        return false;
    }

    true
}
<commit_msg>Support hit testing for ImageMasks<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/. */

use api::{BorderRadius, ComplexClipRegion, DeviceIntRect, ImageMask, ImageRendering, LayerPoint};
use api::{ClipMode, LayerRect, LayerSize};
use api::{LayerToWorldTransform, LayoutPoint, LayoutVector2D, LocalClip};
use border::BorderCornerClipSource;
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use prim_store::{ClipData, ImageMaskData};
use resource_cache::ResourceCache;
use util::{extract_inner_rect_safe, TransformedRect};

pub const MAX_CLIP: f32 = 1000000.0;

pub type ClipStore = FreeList<ClipSources>;
pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
pub type ClipSourcesWeakHandle = WeakFreeListHandle<ClipSources>;

#[derive(Clone, Debug)]
pub struct ClipRegion {
    pub main: LayerRect,
    pub image_mask: Option<ImageMask>,
    pub complex_clips: Vec<ComplexClipRegion>,
}

impl ClipRegion {
    pub fn create_for_clip_node(
        rect: LayerRect,
        mut complex_clips: Vec<ComplexClipRegion>,
        mut image_mask: Option<ImageMask>,
        reference_frame_relative_offset: &LayoutVector2D,
    ) -> ClipRegion {
        let rect = rect.translate(reference_frame_relative_offset);

        if let Some(ref mut image_mask) = image_mask {
            image_mask.rect = image_mask.rect.translate(reference_frame_relative_offset);
        }

        for complex_clip in complex_clips.iter_mut() {
            complex_clip.rect = complex_clip.rect.translate(reference_frame_relative_offset);
        }

        ClipRegion {
            main: rect,
            image_mask,
            complex_clips,
        }
    }

    pub fn create_for_clip_node_with_local_clip(
        local_clip: &LocalClip,
        reference_frame_relative_offset: &LayoutVector2D
    ) -> ClipRegion {
        let complex_clips = match local_clip {
            &LocalClip::Rect(_) => Vec::new(),
            &LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
        };
        ClipRegion::create_for_clip_node(
            *local_clip.clip_rect(),
            complex_clips,
            None,
            reference_frame_relative_offset
        )
    }
}

#[derive(Debug)]
pub enum ClipSource {
    Rectangle(LayerRect),
    RoundedRectangle(LayerRect, BorderRadius, ClipMode),
    Image(ImageMask),
    /// TODO(gw): This currently only handles dashed style
    /// clips, where the border style is dashed for both
    /// adjacent border edges. Expand to handle dotted style
    /// and different styles per edge.
    BorderCorner(BorderCornerClipSource),
}

impl From<ClipRegion> for ClipSources {
    fn from(region: ClipRegion) -> ClipSources {
        let mut clips = Vec::new();

        if let Some(info) = region.image_mask {
            clips.push(ClipSource::Image(info));
        }

        clips.push(ClipSource::Rectangle(region.main));

        for complex in region.complex_clips {
            clips.push(ClipSource::RoundedRectangle(
                complex.rect,
                complex.radii,
                complex.mode,
            ));
        }

        ClipSources::new(clips)
    }
}

impl ClipSource {
    pub fn contains(&self, point: &LayerPoint) -> bool {
        // We currently do not handle all BorderCorners, because they aren't used for
        // ClipScrollNodes and this method is only used during hit testing.
        match self {
            &ClipSource::Rectangle(ref rectangle) => rectangle.contains(point),
            &ClipSource::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            &ClipSource::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
            &ClipSource::Image(mask) => mask.rect.contains(point),
            &ClipSource::BorderCorner(_) =>
                unreachable!("Tried to call contains on a BorderCornerr."),
        }
    }

}

#[derive(Debug)]
pub struct ClipSources {
    pub clips: Vec<(ClipSource, GpuCacheHandle)>,
    pub bounds: MaskBounds,
}

impl ClipSources {
    pub fn new(clips: Vec<ClipSource>) -> ClipSources {
        let clips = clips
            .into_iter()
            .map(|clip| (clip, GpuCacheHandle::new()))
            .collect();

        ClipSources {
            clips,
            bounds: MaskBounds {
                inner: None,
                outer: None,
            },
        }
    }

    pub fn clips(&self) -> &[(ClipSource, GpuCacheHandle)] {
        &self.clips
    }

    pub fn update(
        &mut self,
        layer_transform: &LayerToWorldTransform,
        gpu_cache: &mut GpuCache,
        resource_cache: &mut ResourceCache,
        device_pixel_ratio: f32,
    ) {
        if self.clips.is_empty() {
            return;
        }

        // compute the local bounds
        if self.bounds.inner.is_none() {
            let mut local_rect = Some(LayerRect::new(
                LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
                LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP),
            ));
            let mut local_inner = local_rect;
            let mut has_clip_out = false;
            let mut has_border_clip = false;

            for &(ref source, _) in &self.clips {
                match *source {
                    ClipSource::Image(ref mask) => {
                        if !mask.repeat {
                            local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
                        }
                        local_inner = None;
                    }
                    ClipSource::Rectangle(rect) => {
                        local_rect = local_rect.and_then(|r| r.intersection(&rect));
                        local_inner = local_inner.and_then(|r| r.intersection(&rect));
                    }
                    ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
                        // Once we encounter a clip-out, we just assume the worst
                        // case clip mask size, for now.
                        if mode == ClipMode::ClipOut {
                            has_clip_out = true;
                        }

                        local_rect = local_rect.and_then(|r| r.intersection(rect));

                        let inner_rect = extract_inner_rect_safe(rect, radius);
                        local_inner = local_inner
                            .and_then(|r| inner_rect.and_then(|ref inner| r.intersection(inner)));
                    }
                    ClipSource::BorderCorner { .. } => {
                        has_border_clip = true;
                    }
                }
            }

            // Work out the type of mask geometry we have, based on the
            // list of clip sources above.
            self.bounds = if has_clip_out || has_border_clip {
                // For clip-out, the mask rect is not known.
                MaskBounds {
                    outer: None,
                    inner: Some(LayerRect::zero().into()),
                }
            } else {
                MaskBounds {
                    outer: Some(local_rect.unwrap_or(LayerRect::zero()).into()),
                    inner: Some(local_inner.unwrap_or(LayerRect::zero()).into()),
                }
            };
        }

        // update the screen bounds
        self.bounds.update(layer_transform, device_pixel_ratio);

        for &mut (ref mut source, ref mut handle) in &mut self.clips {
            if let Some(mut request) = gpu_cache.request(handle) {
                match *source {
                    ClipSource::Image(ref mask) => {
                        let data = ImageMaskData {
                            local_rect: mask.rect,
                        };
                        data.write_gpu_blocks(request);
                    }
                    ClipSource::Rectangle(rect) => {
                        let data = ClipData::uniform(rect, 0.0, ClipMode::Clip);
                        data.write(&mut request);
                    }
                    ClipSource::RoundedRectangle(ref rect, ref radius, mode) => {
                        let data = ClipData::rounded_rect(rect, radius, mode);
                        data.write(&mut request);
                    }
                    ClipSource::BorderCorner(ref mut source) => {
                        source.write(request);
                    }
                }
            }
        }

        for &(ref clip, _) in &self.clips {
            if let ClipSource::Image(ref mask) = *clip {
                resource_cache.request_image(mask.image, ImageRendering::Auto, None, gpu_cache);
            }
        }
    }

    /// Whether or not this ClipSources has any clips (does any clipping).
    pub fn has_clips(&self) -> bool {
        !self.clips.is_empty()
    }
}

/// Represents a local rect and a device space
/// rectangles that are either outside or inside bounds.
#[derive(Clone, Debug, PartialEq)]
pub struct Geometry {
    pub local_rect: LayerRect,
    pub device_rect: DeviceIntRect,
}

impl From<LayerRect> for Geometry {
    fn from(local_rect: LayerRect) -> Self {
        Geometry {
            local_rect,
            device_rect: DeviceIntRect::zero(),
        }
    }
}

/// Depending on the complexity of the clip, we may either
/// know the outer and/or inner rect, or neither or these.
/// In the case of a clip-out, we currently set the mask
/// bounds to be unknown. This is conservative, but ensures
/// correctness. In the future we can make this a lot
/// more clever with some proper region handling.
#[derive(Clone, Debug, PartialEq)]
pub struct MaskBounds {
    pub outer: Option<Geometry>,
    pub inner: Option<Geometry>,
}

impl MaskBounds {
    pub fn update(&mut self, transform: &LayerToWorldTransform, device_pixel_ratio: f32) {
        if let Some(ref mut outer) = self.outer {
            let transformed =
                TransformedRect::new(&outer.local_rect, transform, device_pixel_ratio);
            outer.device_rect = transformed.bounding_rect;
        }
        if let Some(ref mut inner) = self.inner {
            let transformed =
                TransformedRect::new(&inner.local_rect, transform, device_pixel_ratio);
            inner.device_rect = transformed.inner_rect;
        }
    }
}

pub trait Contains {
    fn contains(&self, point: &LayoutPoint) -> bool;
}

impl Contains for LocalClip {
    fn contains(&self, point: &LayoutPoint) -> bool {
        if !self.clip_rect().contains(point) {
            return false;
        }
        match self {
            &LocalClip::Rect(..) => true,
            &LocalClip::RoundedRect(_, complex_clip) => complex_clip.contains(point),
        }
    }
}

impl Contains for ComplexClipRegion {
    fn contains(&self, point: &LayoutPoint) -> bool {
        rounded_rectangle_contains_point(point, &self.rect, &self.radii)
    }
}

fn rounded_rectangle_contains_point(point: &LayoutPoint,
                                    rect: &LayerRect,
                                    radii: &BorderRadius)
                                    -> bool {
    if !rect.contains(point) {
        return false;
    }

    let top_left_center = rect.origin + radii.top_left.to_vector();
    if top_left_center.x > point.x && top_left_center.y > point.y &&
       !Ellipse::new(radii.top_left).contains(*point - top_left_center.to_vector()) {
        return false;
    }

    let bottom_right_center = rect.bottom_right() - radii.bottom_right.to_vector();
    if bottom_right_center.x < point.x && bottom_right_center.y < point.y &&
       !Ellipse::new(radii.bottom_right).contains(*point - bottom_right_center.to_vector()) {
        return false;
    }

    let top_right_center = rect.top_right() +
                           LayoutVector2D::new(-radii.top_right.width, radii.top_right.height);
    if top_right_center.x < point.x && top_right_center.y > point.y &&
       !Ellipse::new(radii.top_right).contains(*point - top_right_center.to_vector()) {
        return false;
    }

    let bottom_left_center = rect.bottom_left() +
                             LayoutVector2D::new(radii.bottom_left.width, -radii.bottom_left.height);
    if bottom_left_center.x > point.x && bottom_left_center.y < point.y &&
       !Ellipse::new(radii.bottom_left).contains(*point - bottom_left_center.to_vector()) {
        return false;
    }

    true
}
<|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/. */

use std::f32::consts::{FRAC_1_SQRT_2};
use euclid::{Point2D, Rect, Size2D};
use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedMatrix4D};
use webrender_traits::{DeviceIntRect, DeviceIntPoint, DeviceIntSize};
use webrender_traits::{LayerRect, WorldPoint4D, LayerPoint4D, LayerToWorldTransform};
use webrender_traits::{BorderRadius, ComplexClipRegion, LayoutRect};
use num_traits::Zero;

// Matches the definition of SK_ScalarNearlyZero in Skia.
const NEARLY_ZERO: f32 = 1.0 / 4096.0;

// TODO: Implement these in euclid!
pub trait MatrixHelpers<Src, Dst> {
    fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst>;
    fn is_identity(&self) -> bool;
    fn preserves_2d_axis_alignment(&self) -> bool;
}

impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedMatrix4D<f32, Src, Dst> {
    fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst> {
        let top_left = self.transform_point(&rect.origin);
        let top_right = self.transform_point(&rect.top_right());
        let bottom_left = self.transform_point(&rect.bottom_left());
        let bottom_right = self.transform_point(&rect.bottom_right());
        TypedRect::from_points(&[top_left, top_right, bottom_right, bottom_left])
    }

    fn is_identity(&self) -> bool {
        *self == TypedMatrix4D::identity()
    }

    // A port of the preserves2dAxisAlignment function in Skia.
    // Defined in the SkMatrix44 class.
    fn preserves_2d_axis_alignment(&self) -> bool {
        if self.m14 != 0.0 || self.m24 != 0.0 {
            return false;
        }

        let mut col0 = 0;
        let mut col1 = 0;
        let mut row0 = 0;
        let mut row1 = 0;

        if self.m11.abs() > NEARLY_ZERO {
            col0 += 1;
            row0 += 1;
        }
        if self.m12.abs() > NEARLY_ZERO {
            col1 += 1;
            row0 += 1;
        }
        if self.m21.abs() > NEARLY_ZERO {
            col0 += 1;
            row1 += 1;
        }
        if self.m22.abs() > NEARLY_ZERO {
            col1 += 1;
            row1 += 1;
        }

        col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2
    }
}

pub trait RectHelpers<U> where Self: Sized {
    fn contains_rect(&self, other: &Self) -> bool;
    fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self;
    fn is_well_formed_and_nonempty(&self) -> bool;
}

impl<U> RectHelpers<U> for TypedRect<f32, U> {

    fn contains_rect(&self, other: &Self) -> bool {
        self.origin.x <= other.origin.x &&
        self.origin.y <= other.origin.y &&
        self.max_x() >= other.max_x() &&
        self.max_y() >= other.max_y()
    }

    fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
        TypedRect::new(TypedPoint2D::new(x0, y0),
                       TypedSize2D::new(x1 - x0, y1 - y0))
    }

    fn is_well_formed_and_nonempty(&self) -> bool {
        self.size.width > 0.0 && self.size.height > 0.0
    }
}

// Don't use `euclid`'s `is_empty` because that has effectively has an "and" in the conditional
// below instead of an "or".
pub fn rect_is_empty<N:PartialEq + Zero, U>(rect: &TypedRect<N, U>) -> bool {
    rect.size.width == Zero::zero() || rect.size.height == Zero::zero()
}

#[inline]
pub fn rect_from_points_f(x0: f32,
                          y0: f32,
                          x1: f32,
                          y1: f32) -> Rect<f32> {
    Rect::new(Point2D::new(x0, y0),
              Size2D::new(x1 - x0, y1 - y0))
}

pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
    (b - a) * t + a
}

pub fn subtract_rect<U>(rect: &TypedRect<f32, U>,
                        other: &TypedRect<f32, U>,
                        results: &mut Vec<TypedRect<f32, U>>) {
    results.clear();

    let int = rect.intersection(other);
    match int {
        Some(int) => {
            let rx0 = rect.origin.x;
            let ry0 = rect.origin.y;
            let rx1 = rx0 + rect.size.width;
            let ry1 = ry0 + rect.size.height;

            let ox0 = int.origin.x;
            let oy0 = int.origin.y;
            let ox1 = ox0 + int.size.width;
            let oy1 = oy0 + int.size.height;

            let r = TypedRect::from_untyped(&rect_from_points_f(rx0, ry0, ox0, ry1));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
            let r = TypedRect::from_untyped(&rect_from_points_f(ox0, ry0, ox1, oy0));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
            let r = TypedRect::from_untyped(&rect_from_points_f(ox0, oy1, ox1, ry1));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
            let r = TypedRect::from_untyped(&rect_from_points_f(ox1, ry0, rx1, ry1));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
        }
        None => {
            results.push(*rect);
        }
    }
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum TransformedRectKind {
    AxisAligned = 0,
    Complex = 1,
}

#[derive(Debug, Clone)]
pub struct TransformedRect {
    pub local_rect: LayerRect,
    pub bounding_rect: DeviceIntRect,
    pub inner_rect: DeviceIntRect,
    pub vertices: [WorldPoint4D; 4],
    pub kind: TransformedRectKind,
}

impl TransformedRect {
    pub fn new(rect: &LayerRect,
               transform: &LayerToWorldTransform,
               device_pixel_ratio: f32) -> TransformedRect {

        let kind = if transform.preserves_2d_axis_alignment() {
            TransformedRectKind::AxisAligned
        } else {
            TransformedRectKind::Complex
        };

        // FIXME(gw): This code is meant to be a fast path for simple transforms.
        // However, it fails on transforms that translate Z but result in an
        // axis aligned rect.

/*
        match kind {
            TransformedRectKind::AxisAligned => {
                let v0 = transform.transform_point(&rect.origin);
                let v1 = transform.transform_point(&rect.top_right());
                let v2 = transform.transform_point(&rect.bottom_left());
                let v3 = transform.transform_point(&rect.bottom_right());

                let screen_min_dp = Point2D::new(DevicePixel((v0.x * device_pixel_ratio).floor() as i32),
                                                 DevicePixel((v0.y * device_pixel_ratio).floor() as i32));
                let screen_max_dp = Point2D::new(DevicePixel((v3.x * device_pixel_ratio).ceil() as i32),
                                                 DevicePixel((v3.y * device_pixel_ratio).ceil() as i32));

                let screen_rect_dp = Rect::new(screen_min_dp, Size2D::new(screen_max_dp.x - screen_min_dp.x,
                                                                          screen_max_dp.y - screen_min_dp.y));

                TransformedRect {
                    local_rect: *rect,
                    vertices: [
                        Point4D::new(v0.x, v0.y, 0.0, 1.0),
                        Point4D::new(v1.x, v1.y, 0.0, 1.0),
                        Point4D::new(v2.x, v2.y, 0.0, 1.0),
                        Point4D::new(v3.x, v3.y, 0.0, 1.0),
                    ],
                    bounding_rect: screen_rect_dp,
                    kind: kind,
                }
            }
            TransformedRectKind::Complex => {
                */
                let vertices = [
                    transform.transform_point4d(&LayerPoint4D::new(rect.origin.x,
                                                                   rect.origin.y,
                                                                   0.0,
                                                                   1.0)),
                    transform.transform_point4d(&LayerPoint4D::new(rect.bottom_left().x,
                                                                   rect.bottom_left().y,
                                                                   0.0,
                                                                   1.0)),
                    transform.transform_point4d(&LayerPoint4D::new(rect.bottom_right().x,
                                                                   rect.bottom_right().y,
                                                                   0.0,
                                                                   1.0)),
                    transform.transform_point4d(&LayerPoint4D::new(rect.top_right().x,
                                                                   rect.top_right().y,
                                                                   0.0,
                                                                   1.0)),
                ];

                let (mut xs, mut ys) = ([0.0; 4], [0.0; 4]);

                for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) {
                    let inv_w = 1.0 / vertex.w;
                    *x = vertex.x * inv_w;
                    *y = vertex.y * inv_w;
                }

                xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
                ys.sort_by(|a, b| a.partial_cmp(b).unwrap());

                let outer_min_dp = DeviceIntPoint::new((xs[0] * device_pixel_ratio).floor() as i32,
                                                       (ys[0] * device_pixel_ratio).floor() as i32);
                let outer_max_dp = DeviceIntPoint::new((xs[3] * device_pixel_ratio).ceil() as i32,
                                                       (ys[3] * device_pixel_ratio).ceil() as i32);
                let inner_min_dp = DeviceIntPoint::new((xs[1] * device_pixel_ratio).ceil() as i32,
                                                       (ys[1] * device_pixel_ratio).ceil() as i32);
                let inner_max_dp = DeviceIntPoint::new((xs[2] * device_pixel_ratio).floor() as i32,
                                                       (ys[2] * device_pixel_ratio).floor() as i32);

                TransformedRect {
                    local_rect: *rect,
                    vertices: vertices,
                    bounding_rect: DeviceIntRect::new(outer_min_dp,
                                                      DeviceIntSize::new(outer_max_dp.x - outer_min_dp.x,
                                                                         outer_max_dp.y - outer_min_dp.y)),
                    inner_rect: DeviceIntRect::new(inner_min_dp,
                                                   DeviceIntSize::new(inner_max_dp.x - inner_min_dp.x,
                                                                      inner_max_dp.y - inner_min_dp.y)),
                    kind: kind,
                }
                /*
            }
        }*/
    }
}

#[inline(always)]
pub fn pack_as_float(value: u32) -> f32 {
    value as f32 + 0.5
}

pub trait ComplexClipRegionHelpers {
    /// Return an aligned rectangle that is inside the clip region and doesn't intersect
    /// any of the bounding rectangles of the rounded corners.
    fn get_inner_rect_safe(&self) -> Option<LayoutRect>;
    /// Return the approximately largest aligned rectangle that is fully inside
    /// the provided clip region.
    fn get_inner_rect_full(&self) -> Option<LayoutRect>;
}

impl ComplexClipRegionHelpers for ComplexClipRegion {
    fn get_inner_rect_safe(&self) -> Option<LayoutRect> {
        // value of `k==1.0` is used for extraction of the corner rectangles
        // see `SEGMENT_CORNER_*` in `clip_shared.glsl`
        extract_inner_rect_impl(&self.rect, &self.radii, 1.0)
    }

    fn get_inner_rect_full(&self) -> Option<LayoutRect> {
        // this `k` optimal for a simple case of all border radii being equal
        let k = 1.0 - 0.5 * FRAC_1_SQRT_2; // could be nicely approximated to `0.3`
        extract_inner_rect_impl(&self.rect, &self.radii, k)
    }
}

#[inline]
fn extract_inner_rect_impl<U>(rect: &TypedRect<f32, U>,
                              radii: &BorderRadius,
                              k: f32) -> Option<TypedRect<f32, U>> {
    // `k` defines how much border is taken into account
    // We enforce the offsets to be rounded to pixel boundaries
    // by `ceil`-ing and `floor`-ing them

    let xl = (k * radii.top_left.width.max(radii.bottom_left.width)).ceil();
    let xr = (rect.size.width - k * radii.top_right.width.max(radii.bottom_right.width)).floor();
    let yt = (k * radii.top_left.height.max(radii.top_right.height)).ceil();
    let yb = (rect.size.height - k * radii.bottom_left.height.max(radii.bottom_right.height)).floor();

    if xl <= xr && yt <= yb {
        Some(TypedRect::new(TypedPoint2D::new(rect.origin.x + xl, rect.origin.y + yt),
             TypedSize2D::new(xr-xl, yb-yt)))
    } else {
        None
    }
}

/// Consumes the old vector and returns a new one that may reuse the old vector's allocated
/// memory.
pub fn recycle_vec<T>(mut old_vec: Vec<T>) -> Vec<T> {
    if old_vec.capacity() > 2 * old_vec.len() {
        // Avoid reusing the buffer if it is a lot larger than it needs to be. This prevents
        // a frame with exceptionally large allocations to cause subsequent frames to retain
        // more memory than they need.
        return Vec::with_capacity(old_vec.len());
    }

    old_vec.clear();

    return old_vec;
}
<commit_msg>Ensure the result of calculation is finite number.<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/. */

use std::f32::consts::{FRAC_1_SQRT_2};
use euclid::{Point2D, Rect, Size2D};
use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedMatrix4D};
use webrender_traits::{DeviceIntRect, DeviceIntPoint, DeviceIntSize};
use webrender_traits::{LayerRect, WorldPoint4D, LayerPoint4D, LayerToWorldTransform};
use webrender_traits::{BorderRadius, ComplexClipRegion, LayoutRect};
use num_traits::Zero;

// Matches the definition of SK_ScalarNearlyZero in Skia.
const NEARLY_ZERO: f32 = 1.0 / 4096.0;

// TODO: Implement these in euclid!
pub trait MatrixHelpers<Src, Dst> {
    fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst>;
    fn is_identity(&self) -> bool;
    fn preserves_2d_axis_alignment(&self) -> bool;
}

impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedMatrix4D<f32, Src, Dst> {
    fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst> {
        let top_left = self.transform_point(&rect.origin);
        let top_right = self.transform_point(&rect.top_right());
        let bottom_left = self.transform_point(&rect.bottom_left());
        let bottom_right = self.transform_point(&rect.bottom_right());
        TypedRect::from_points(&[top_left, top_right, bottom_right, bottom_left])
    }

    fn is_identity(&self) -> bool {
        *self == TypedMatrix4D::identity()
    }

    // A port of the preserves2dAxisAlignment function in Skia.
    // Defined in the SkMatrix44 class.
    fn preserves_2d_axis_alignment(&self) -> bool {
        if self.m14 != 0.0 || self.m24 != 0.0 {
            return false;
        }

        let mut col0 = 0;
        let mut col1 = 0;
        let mut row0 = 0;
        let mut row1 = 0;

        if self.m11.abs() > NEARLY_ZERO {
            col0 += 1;
            row0 += 1;
        }
        if self.m12.abs() > NEARLY_ZERO {
            col1 += 1;
            row0 += 1;
        }
        if self.m21.abs() > NEARLY_ZERO {
            col0 += 1;
            row1 += 1;
        }
        if self.m22.abs() > NEARLY_ZERO {
            col1 += 1;
            row1 += 1;
        }

        col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2
    }
}

pub trait RectHelpers<U> where Self: Sized {
    fn contains_rect(&self, other: &Self) -> bool;
    fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self;
    fn is_well_formed_and_nonempty(&self) -> bool;
}

impl<U> RectHelpers<U> for TypedRect<f32, U> {

    fn contains_rect(&self, other: &Self) -> bool {
        self.origin.x <= other.origin.x &&
        self.origin.y <= other.origin.y &&
        self.max_x() >= other.max_x() &&
        self.max_y() >= other.max_y()
    }

    fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
        TypedRect::new(TypedPoint2D::new(x0, y0),
                       TypedSize2D::new(x1 - x0, y1 - y0))
    }

    fn is_well_formed_and_nonempty(&self) -> bool {
        self.size.width > 0.0 && self.size.height > 0.0
    }
}

// Don't use `euclid`'s `is_empty` because that has effectively has an "and" in the conditional
// below instead of an "or".
pub fn rect_is_empty<N:PartialEq + Zero, U>(rect: &TypedRect<N, U>) -> bool {
    rect.size.width == Zero::zero() || rect.size.height == Zero::zero()
}

#[inline]
pub fn rect_from_points_f(x0: f32,
                          y0: f32,
                          x1: f32,
                          y1: f32) -> Rect<f32> {
    Rect::new(Point2D::new(x0, y0),
              Size2D::new(x1 - x0, y1 - y0))
}

pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
    (b - a) * t + a
}

pub fn subtract_rect<U>(rect: &TypedRect<f32, U>,
                        other: &TypedRect<f32, U>,
                        results: &mut Vec<TypedRect<f32, U>>) {
    results.clear();

    let int = rect.intersection(other);
    match int {
        Some(int) => {
            let rx0 = rect.origin.x;
            let ry0 = rect.origin.y;
            let rx1 = rx0 + rect.size.width;
            let ry1 = ry0 + rect.size.height;

            let ox0 = int.origin.x;
            let oy0 = int.origin.y;
            let ox1 = ox0 + int.size.width;
            let oy1 = oy0 + int.size.height;

            let r = TypedRect::from_untyped(&rect_from_points_f(rx0, ry0, ox0, ry1));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
            let r = TypedRect::from_untyped(&rect_from_points_f(ox0, ry0, ox1, oy0));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
            let r = TypedRect::from_untyped(&rect_from_points_f(ox0, oy1, ox1, ry1));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
            let r = TypedRect::from_untyped(&rect_from_points_f(ox1, ry0, rx1, ry1));
            if r.size.width > 0.0 && r.size.height > 0.0 {
                results.push(r);
            }
        }
        None => {
            results.push(*rect);
        }
    }
}

pub fn get_normal(x: f32) -> Option<f32> {
    if x.is_normal() {
        Some(x)
    } else {
        None
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
pub enum TransformedRectKind {
    AxisAligned = 0,
    Complex = 1,
}

#[derive(Debug, Clone)]
pub struct TransformedRect {
    pub local_rect: LayerRect,
    pub bounding_rect: DeviceIntRect,
    pub inner_rect: DeviceIntRect,
    pub vertices: [WorldPoint4D; 4],
    pub kind: TransformedRectKind,
}

impl TransformedRect {
    pub fn new(rect: &LayerRect,
               transform: &LayerToWorldTransform,
               device_pixel_ratio: f32) -> TransformedRect {

        let kind = if transform.preserves_2d_axis_alignment() {
            TransformedRectKind::AxisAligned
        } else {
            TransformedRectKind::Complex
        };

        // FIXME(gw): This code is meant to be a fast path for simple transforms.
        // However, it fails on transforms that translate Z but result in an
        // axis aligned rect.

/*
        match kind {
            TransformedRectKind::AxisAligned => {
                let v0 = transform.transform_point(&rect.origin);
                let v1 = transform.transform_point(&rect.top_right());
                let v2 = transform.transform_point(&rect.bottom_left());
                let v3 = transform.transform_point(&rect.bottom_right());

                let screen_min_dp = Point2D::new(DevicePixel((v0.x * device_pixel_ratio).floor() as i32),
                                                 DevicePixel((v0.y * device_pixel_ratio).floor() as i32));
                let screen_max_dp = Point2D::new(DevicePixel((v3.x * device_pixel_ratio).ceil() as i32),
                                                 DevicePixel((v3.y * device_pixel_ratio).ceil() as i32));

                let screen_rect_dp = Rect::new(screen_min_dp, Size2D::new(screen_max_dp.x - screen_min_dp.x,
                                                                          screen_max_dp.y - screen_min_dp.y));

                TransformedRect {
                    local_rect: *rect,
                    vertices: [
                        Point4D::new(v0.x, v0.y, 0.0, 1.0),
                        Point4D::new(v1.x, v1.y, 0.0, 1.0),
                        Point4D::new(v2.x, v2.y, 0.0, 1.0),
                        Point4D::new(v3.x, v3.y, 0.0, 1.0),
                    ],
                    bounding_rect: screen_rect_dp,
                    kind: kind,
                }
            }
            TransformedRectKind::Complex => {
                */
                let vertices = [
                    transform.transform_point4d(&LayerPoint4D::new(rect.origin.x,
                                                                   rect.origin.y,
                                                                   0.0,
                                                                   1.0)),
                    transform.transform_point4d(&LayerPoint4D::new(rect.bottom_left().x,
                                                                   rect.bottom_left().y,
                                                                   0.0,
                                                                   1.0)),
                    transform.transform_point4d(&LayerPoint4D::new(rect.bottom_right().x,
                                                                   rect.bottom_right().y,
                                                                   0.0,
                                                                   1.0)),
                    transform.transform_point4d(&LayerPoint4D::new(rect.top_right().x,
                                                                   rect.top_right().y,
                                                                   0.0,
                                                                   1.0)),
                ];

                let (mut xs, mut ys) = ([0.0; 4], [0.0; 4]);

                for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) {
                    let inv_w = 1.0 / vertex.w;
                    *x = get_normal(vertex.x * inv_w).unwrap_or(0.0);
                    *y = get_normal(vertex.y * inv_w).unwrap_or(0.0);
                }

                xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
                ys.sort_by(|a, b| a.partial_cmp(b).unwrap());

                let outer_min_dp = DeviceIntPoint::new((xs[0] * device_pixel_ratio).floor() as i32,
                                                       (ys[0] * device_pixel_ratio).floor() as i32);
                let outer_max_dp = DeviceIntPoint::new((xs[3] * device_pixel_ratio).ceil() as i32,
                                                       (ys[3] * device_pixel_ratio).ceil() as i32);
                let inner_min_dp = DeviceIntPoint::new((xs[1] * device_pixel_ratio).ceil() as i32,
                                                       (ys[1] * device_pixel_ratio).ceil() as i32);
                let inner_max_dp = DeviceIntPoint::new((xs[2] * device_pixel_ratio).floor() as i32,
                                                       (ys[2] * device_pixel_ratio).floor() as i32);

                TransformedRect {
                    local_rect: *rect,
                    vertices: vertices,
                    bounding_rect: DeviceIntRect::new(outer_min_dp,
                                                      DeviceIntSize::new(outer_max_dp.x - outer_min_dp.x,
                                                                         outer_max_dp.y - outer_min_dp.y)),
                    inner_rect: DeviceIntRect::new(inner_min_dp,
                                                   DeviceIntSize::new(inner_max_dp.x - inner_min_dp.x,
                                                                      inner_max_dp.y - inner_min_dp.y)),
                    kind: kind,
                }
                /*
            }
        }*/
    }
}

#[inline(always)]
pub fn pack_as_float(value: u32) -> f32 {
    value as f32 + 0.5
}

pub trait ComplexClipRegionHelpers {
    /// Return an aligned rectangle that is inside the clip region and doesn't intersect
    /// any of the bounding rectangles of the rounded corners.
    fn get_inner_rect_safe(&self) -> Option<LayoutRect>;
    /// Return the approximately largest aligned rectangle that is fully inside
    /// the provided clip region.
    fn get_inner_rect_full(&self) -> Option<LayoutRect>;
}

impl ComplexClipRegionHelpers for ComplexClipRegion {
    fn get_inner_rect_safe(&self) -> Option<LayoutRect> {
        // value of `k==1.0` is used for extraction of the corner rectangles
        // see `SEGMENT_CORNER_*` in `clip_shared.glsl`
        extract_inner_rect_impl(&self.rect, &self.radii, 1.0)
    }

    fn get_inner_rect_full(&self) -> Option<LayoutRect> {
        // this `k` optimal for a simple case of all border radii being equal
        let k = 1.0 - 0.5 * FRAC_1_SQRT_2; // could be nicely approximated to `0.3`
        extract_inner_rect_impl(&self.rect, &self.radii, k)
    }
}

#[inline]
fn extract_inner_rect_impl<U>(rect: &TypedRect<f32, U>,
                              radii: &BorderRadius,
                              k: f32) -> Option<TypedRect<f32, U>> {
    // `k` defines how much border is taken into account
    // We enforce the offsets to be rounded to pixel boundaries
    // by `ceil`-ing and `floor`-ing them

    let xl = (k * radii.top_left.width.max(radii.bottom_left.width)).ceil();
    let xr = (rect.size.width - k * radii.top_right.width.max(radii.bottom_right.width)).floor();
    let yt = (k * radii.top_left.height.max(radii.top_right.height)).ceil();
    let yb = (rect.size.height - k * radii.bottom_left.height.max(radii.bottom_right.height)).floor();

    if xl <= xr && yt <= yb {
        Some(TypedRect::new(TypedPoint2D::new(rect.origin.x + xl, rect.origin.y + yt),
             TypedSize2D::new(xr-xl, yb-yt)))
    } else {
        None
    }
}

/// Consumes the old vector and returns a new one that may reuse the old vector's allocated
/// memory.
pub fn recycle_vec<T>(mut old_vec: Vec<T>) -> Vec<T> {
    if old_vec.capacity() > 2 * old_vec.len() {
        // Avoid reusing the buffer if it is a lot larger than it needs to be. This prevents
        // a frame with exceptionally large allocations to cause subsequent frames to retain
        // more memory than they need.
        return Vec::with_capacity(old_vec.len());
    }

    old_vec.clear();

    return old_vec;
}
<|endoftext|>


<commit_before>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};
use num::traits::FromPrimitive;

use {Error, Result};
use information_element::SessionStatus;
use message::Message;

/// A mobile originated (MO) message;
#[derive(Debug)]
pub struct MobileOriginated {
    cdr_reference: u32,
    imei: [u8; 15],
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time: u32,
    payload: Vec<u8>,
}

impl Default for MobileOriginated {
    fn default() -> MobileOriginated {
        MobileOriginated {
            cdr_reference: 0,
            imei: [0; 15],
            session_status: SessionStatus::Unknown,
            momsn: 0,
            mtmsn: 0,
            time: 0,
            payload: Vec::new(),
        }
    }
}

impl MobileOriginated {

    /// Reads a mobile originated message from a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// MobileOriginated::from_path("data/0-mo.sbd");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::from_path(path)).into_mobile_originated()))
    }

    /// Returns the cdr reference, also called the auto ID.
    ///
    /// This is a unique value given each call data record.
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }

    /// Returns the IMEI number.
    ///
    /// This is unique to each unit, and is always 15 characters long.
    pub fn imei(&self) -> &[u8] {
        &self.imei
    }

    /// Returns the status of the mobile originated session.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }

    /// Returns the mobile originated message sequence number.
    ///
    /// This value is set by the IMEI, and is incremented for every successful SBD session.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }

    /// Returns the mobile terminated message squence number.
    ///
    /// This value is set by the Gateway when the message is queued for delivery. The MTMSN is
    /// tranferred to the IMEI as part of the MT payload transfer, regardless of the session
    /// success.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }

    /// Returns the UTC timestamp in the form of an epoch integer.
    ///
    /// This is the time of the session between the IMEI and the Gateway, in seconds since the
    /// start of the epoch: 1/1/1970 00:00:00.
    pub fn time(&self) -> u32 {
        self.time
    }

    /// Returns the MO payload.
    pub fn payload(&self) -> &Vec<u8> {
        &self.payload
    }
}

impl Message {

    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        let mut information_elements = self.into_information_elements();

        let header = match information_elements.remove(&1) {
            Some(header) => header,
            None => return Err(Error::NoMobileOriginatedHeader),
        };
        let payload = match information_elements.remove(&2) {
            Some(payload) => payload,
            None => return Err(Error::NoMobileOriginatedPayload),
        };

        let mut readable = header.as_contents_reader();
        let mut mobile_originated: MobileOriginated = Default::default();
        mobile_originated.cdr_reference = try!(readable.read_u32::<BigEndian>());
        let bytes_read = try!(readable.read(&mut mobile_originated.imei));
        if bytes_read != mobile_originated.imei.len() {
            return Err(Error::Undersized(bytes_read));
        }
        mobile_originated.session_status = match SessionStatus::from_u8(try!(readable.read_u8())) {
            Some(status) => status,
            None => SessionStatus::Unknown,
        };
        mobile_originated.momsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.mtmsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.time = try!(readable.read_u32::<BigEndian>());

        mobile_originated.payload = payload.into_contents();

        Ok(mobile_originated)
    }
}

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

    use std::str;

    use information_element::SessionStatus;
    use message::Message;

    #[test]
    fn into_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }

    #[test]
    fn from_path() {
        MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn mo_header_values() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1894516585, mo.cdr_reference());
        assert_eq!("300234063904190", str::from_utf8(mo.imei()).unwrap());
        assert_eq!(SessionStatus::Ok, mo.session_status());
        assert_eq!(75, mo.momsn());
        assert_eq!(0, mo.mtmsn());
        assert_eq!(1436465708, mo.time());
    }

    #[test]
    fn mo_payload() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", str::from_utf8(mo.payload()).unwrap());
    }
}
<commit_msg>Add some todos to mobile originated<commit_after>//! Handling for mobile originated (MO) messages.
//!
//! SBD messages really can come in two forms, mobile-originated and mobile-terminated. This module
//! provides functionality for the MO subset of messages.

use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};
use num::traits::FromPrimitive;

use {Error, Result};
use information_element::SessionStatus;
use message::Message;

/// A mobile originated (MO) message;
#[derive(Debug)]
pub struct MobileOriginated {
    cdr_reference: u32,
    imei: [u8; 15],
    session_status: SessionStatus,
    momsn: u16,
    mtmsn: u16,
    time: u32,
    payload: Vec<u8>,
}

impl Default for MobileOriginated {
    fn default() -> MobileOriginated {
        MobileOriginated {
            cdr_reference: 0,
            imei: [0; 15],
            session_status: SessionStatus::Unknown,
            momsn: 0,
            mtmsn: 0,
            time: 0,
            payload: Vec::new(),
        }
    }
}

impl MobileOriginated {

    /// Reads a mobile originated message from a `Path`.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::mobile_originated::MobileOriginated;
    /// MobileOriginated::from_path("data/0-mo.sbd");
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<MobileOriginated> {
        Ok(try!(try!(Message::from_path(path)).into_mobile_originated()))
    }

    /// Returns the cdr reference, also called the auto ID.
    ///
    /// This is a unique value given each call data record.
    pub fn cdr_reference(&self) -> u32 {
        self.cdr_reference
    }

    /// Returns the IMEI number.
    ///
    /// This is unique to each unit, and is always 15 characters long.
    pub fn imei(&self) -> &[u8] {
        &self.imei
    }

    /// Returns the status of the mobile originated session.
    pub fn session_status(&self) -> SessionStatus {
        self.session_status
    }

    /// Returns the mobile originated message sequence number.
    ///
    /// This value is set by the IMEI, and is incremented for every successful SBD session.
    pub fn momsn(&self) -> u16 {
        self.momsn
    }

    /// Returns the mobile terminated message squence number.
    ///
    /// This value is set by the Gateway when the message is queued for delivery. The MTMSN is
    /// tranferred to the IMEI as part of the MT payload transfer, regardless of the session
    /// success.
    pub fn mtmsn(&self) -> u16 {
        self.mtmsn
    }

    /// Returns the UTC timestamp in the form of an epoch integer.
    ///
    /// This is the time of the session between the IMEI and the Gateway, in seconds since the
    /// start of the epoch: 1/1/1970 00:00:00.
    pub fn time(&self) -> u32 {
        self.time
    }

    /// Returns the MO payload.
    pub fn payload(&self) -> &Vec<u8> {
        &self.payload
    }
}

impl Message {

    /// Convert a message into a mobile originated message.
    ///
    /// Returns an error if this message isn't really mobile originated.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// use sbd::mobile_originated;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// message.into_mobile_originated().unwrap();
    /// ```
    pub fn into_mobile_originated(self) -> Result<MobileOriginated> {
        let mut information_elements = self.into_information_elements();

        // TODO demagicify these numbers
        // TODO should we fail if the payload is absent?
        let header = match information_elements.remove(&1) {
            Some(header) => header,
            None => return Err(Error::NoMobileOriginatedHeader),
        };
        let payload = match information_elements.remove(&2) {
            Some(payload) => payload,
            None => return Err(Error::NoMobileOriginatedPayload),
        };

        let mut readable = header.as_contents_reader();
        let mut mobile_originated: MobileOriginated = Default::default();
        mobile_originated.cdr_reference = try!(readable.read_u32::<BigEndian>());
        let bytes_read = try!(readable.read(&mut mobile_originated.imei));
        if bytes_read != mobile_originated.imei.len() {
            return Err(Error::Undersized(bytes_read));
        }
        mobile_originated.session_status = match SessionStatus::from_u8(try!(readable.read_u8())) {
            Some(status) => status,
            None => SessionStatus::Unknown,
        };
        mobile_originated.momsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.mtmsn = try!(readable.read_u16::<BigEndian>());
        mobile_originated.time = try!(readable.read_u32::<BigEndian>());

        mobile_originated.payload = payload.into_contents();

        Ok(mobile_originated)
    }
}

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

    use std::str;

    use information_element::SessionStatus;
    use message::Message;

    #[test]
    fn into_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        message.into_mobile_originated().unwrap();
    }

    #[test]
    fn from_path() {
        MobileOriginated::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn mo_header_values() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(1894516585, mo.cdr_reference());
        assert_eq!("300234063904190", str::from_utf8(mo.imei()).unwrap());
        assert_eq!(SessionStatus::Ok, mo.session_status());
        assert_eq!(75, mo.momsn());
        assert_eq!(0, mo.mtmsn());
        assert_eq!(1436465708, mo.time());
    }

    #[test]
    fn mo_payload() {
        let mo = MobileOriginated::from_path("data/0-mo.sbd").unwrap();
        assert_eq!("test message from pete", str::from_utf8(mo.payload()).unwrap());
    }
}
<|endoftext|>