<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::apply_transforms::Programs;
use crate::config::{Config, ProjectConfig};
use crate::errors::BuildProjectError;
use common::FileKey;
use graphql_ir::{FragmentDefinition, NamedItem, OperationDefinition};
use graphql_text_printer::{
    print_fragment, print_full_operation, write_operation_with_graphqljs_formatting,
};
use graphql_transforms::MATCH_CONSTANTS;
use interner::StringKey;
use md5::{Digest, Md5};
use persist_query::persist;
use relay_codegen::{build_request_params, Printer};
use relay_typegen::{generate_fragment_type, generate_operation_type};
use signedsource::{sign_file, SIGNING_TOKEN};
use std::fmt::Write;

/// Represents a generated output artifact.
#[derive(Clone)]
pub struct Artifact {
    pub name: StringKey,
    pub content: String,
    pub hash: String,
    /// The source file responsible for generating this file.
    /// For example: `my_project/Component.react.js`
    pub source_file: FileKey,
}

pub async fn generate_artifacts(
    config: &Config,
    project_config: &ProjectConfig,
    programs: &Programs<'_>,
) -> Result<Vec<Artifact>, BuildProjectError> {
    let mut artifacts = Vec::new();
    let mut printer = Printer::default();
    for node in programs.normalization.operations() {
        if let Some(directive) = node
            .directives
            .named(MATCH_CONSTANTS.custom_module_directive_name)
        {
            // Generate normalization file for SplitOperation
            let name_arg = directive
                .arguments
                .named(MATCH_CONSTANTS.derived_from_arg)
                .unwrap();
            let source_name = name_arg
                .value
                .item
                .get_string_literal()
                .expect("Expected `derived_from` argument to be a literal string.");
            let source_node = programs
                .source
                .fragment(source_name)
                .expect("Expected the source document for the SplitOperation to exist.");
            let source_string = print_fragment(programs.source.schema(), &source_node);
            let hash = md5(&source_string);
            artifacts.push(generate_split_operation_artifact(
                &mut printer,
                config,
                programs,
                node,
                &hash,
            ));
        } else {
            let source_node = programs.source.operation(node.name.item).unwrap();
            // TODO: Consider using the std::io::Write trait here to directly
            // write to the md5. Currently, this doesn't work as `write_operation`
            // expects a `std::fmt::Write`.
            // Same for fragment hashing below.
            let mut source_string = String::new();
            write_operation_with_graphqljs_formatting(
                programs.source.schema(),
                &source_node,
                &mut source_string,
            )
            .unwrap();
            let hash = md5(&source_string);
            artifacts.push(
                generate_normalization_artifact(
                    &mut printer,
                    config,
                    project_config,
                    programs,
                    node,
                    &hash,
                )
                .await?,
            );
        }
    }
    for node in programs.reader.fragments() {
        let source_node = programs.source.fragment(node.name.item).unwrap();
        // Same as for operation hashing above.
        let source_string = print_fragment(programs.source.schema(), &source_node);
        let hash = md5(&source_string);
        artifacts.push(generate_reader_artifact(
            &mut printer,
            config,
            programs,
            node,
            &hash,
        ));
    }

    Ok(artifacts)
}

async fn generate_normalization_artifact(
    printer: &mut Printer,
    config: &Config,
    project_config: &ProjectConfig,
    programs: &Programs<'_>,
    node: &OperationDefinition,
    source_hash: &str,
) -> Result<Artifact, BuildProjectError> {
    let name = node.name.item;
    let print_operation_node = programs
        .operation_text
        .operation(name)
        .expect("a query text operation should be generated for this operation");
    let text = print_full_operation(&programs.operation_text, print_operation_node);
    let mut request_parameters = build_request_params(&node);
    let mut operation_hash = None;
    if let Some(ref persist_config) = project_config.persist {
        operation_hash = Some(md5(&text));
        let id = persist(&text, &persist_config.url, &persist_config.params)
            .await
            .map_err(BuildProjectError::PersistError)?;
        request_parameters.id = Some(id);
    } else {
        request_parameters.text = Some(text.clone());
    };
    let reader_operation = programs
        .reader
        .operation(name)
        .expect("a reader fragment should be generated for this operation");
    let operation_fragment = FragmentDefinition {
        name: reader_operation.name,
        variable_definitions: reader_operation.variable_definitions.clone(),
        selections: reader_operation.selections.clone(),
        used_global_variables: Default::default(),
        directives: reader_operation.directives.clone(),
        type_condition: reader_operation.type_,
    };
    let mut content = get_content_start(config);
    writeln!(content, " * {}", SIGNING_TOKEN).unwrap();
    if let Some(operation_hash) = operation_hash {
        writeln!(content, " * @relayHash {}", operation_hash).unwrap();
    }
    writeln!(content, " * @flow").unwrap();
    writeln!(content, " * @lightSyntaxTransform").unwrap();
    writeln!(content, " * @nogrep").unwrap();
    if let Some(codegen_command) = &config.codegen_command {
        writeln!(content, " * @codegen-command: {}", codegen_command).unwrap();
    }
    writeln!(content, " */\n").unwrap();
    writeln!(content, "/* eslint-disable */\n").unwrap();
    writeln!(content, "'use strict';\n").unwrap();
    if let Some(id) = &request_parameters.id {
        writeln!(content, "// @relayRequestID {}\n", id).unwrap();
    }
    let typegen_node = programs
        .typegen
        .operation(name)
        .expect("a type fragment should be generated for this operation");
    writeln!(
        content,
        "/*::\nimport type {{ ConcreteRequest }} from 'relay-runtime';\n{}*/\n",
        generate_operation_type(typegen_node, programs.typegen.schema())
    )
    .unwrap();
    writeln!(content, "/*\n{}*/\n", text).unwrap();
    writeln!(
        content,
        "var node/*: ConcreteRequest*/ = {};\n",
        printer.print_request_deduped(
            programs.normalization.schema(),
            node,
            &operation_fragment,
            request_parameters
        )
    )
    .unwrap();
    writeln!(content, "if (__DEV__) {{").unwrap();
    writeln!(content, "  (node/*: any*/).hash = \"{}\";", source_hash).unwrap();
    writeln!(content, "}}\n").unwrap();
    writeln!(content, "module.exports = node;").unwrap();

    Ok(Artifact {
        name: node.name.item,
        content: sign_file(&content),
        hash: source_hash.to_string(),
        source_file: node.name.location.file(),
    })
}

fn generate_reader_artifact(
    printer: &mut Printer,
    config: &Config,
    programs: &Programs<'_>,
    node: &FragmentDefinition,
    hash: &str,
) -> Artifact {
    let name = node.name.item;
    let mut content = get_content_start(config);
    writeln!(content, " * {}", SIGNING_TOKEN).unwrap();
    writeln!(content, " * @flow").unwrap();
    writeln!(content, " * @lightSyntaxTransform").unwrap();
    writeln!(content, " * @nogrep").unwrap();
    if let Some(codegen_command) = &config.codegen_command {
        writeln!(content, " * @codegen-command: {}", codegen_command).unwrap();
    }
    writeln!(content, " */\n").unwrap();
    writeln!(content, "/* eslint-disable */\n").unwrap();
    writeln!(content, "'use strict';\n").unwrap();
    let typegen_node = programs
        .typegen
        .fragment(name)
        .expect("a type fragment should be generated for this fragment");
    writeln!(
        content,
        "/*::\nimport type {{ ReaderFragment }} from 'relay-runtime';\n{}*/\n",
        generate_fragment_type(typegen_node, programs.typegen.schema())
    )
    .unwrap();
    writeln!(
        content,
        "var node/*: ReaderFragment*/ = {};\n",
        printer.print_fragment_deduped(programs.normalization.schema(), node)
    )
    .unwrap();
    writeln!(content, "if (__DEV__) {{").unwrap();
    writeln!(content, "  (node/*: any*/).hash = \"{}\";", hash).unwrap();
    writeln!(content, "}}\n").unwrap();
    writeln!(content, "module.exports = node;").unwrap();

    Artifact {
        name,
        content: sign_file(&content),
        hash: hash.to_string(),
        source_file: node.name.location.file(),
    }
}

fn generate_split_operation_artifact(
    printer: &mut Printer,
    config: &Config,
    programs: &Programs<'_>,
    node: &OperationDefinition,
    hash: &str,
) -> Artifact {
    let mut content = get_content_start(config);
    writeln!(content, " * {}", SIGNING_TOKEN).unwrap();
    writeln!(content, " * @flow").unwrap();
    writeln!(content, " * @lightSyntaxTransform").unwrap();
    writeln!(content, " * @nogrep").unwrap();
    if let Some(codegen_command) = &config.codegen_command {
        writeln!(content, " * @codegen-command: {}", codegen_command).unwrap();
    }
    writeln!(content, " */\n").unwrap();
    writeln!(content, "/* eslint-disable */\n").unwrap();
    writeln!(content, "'use strict';\n").unwrap();
    writeln!(
        content,
        "/*::\nimport type {{ NormalizationSplitOperation }} from 'relay-runtime';\n\n*/\n"
    )
    .unwrap();
    writeln!(
        content,
        "var node/*: NormalizationSplitOperation*/ = {};\n",
        printer.print_operation_deduped(programs.normalization.schema(), node)
    )
    .unwrap();
    writeln!(content, "if (__DEV__) {{").unwrap();
    writeln!(content, "  (node/*: any*/).hash = \"{}\";", hash).unwrap();
    writeln!(content, "}}\n").unwrap();
    writeln!(content, "module.exports = node;").unwrap();

    Artifact {
        name: node.name.item,
        content: sign_file(&content),
        hash: hash.to_string(),
        source_file: node.name.location.file(),
    }
}

fn get_content_start(config: &Config) -> String {
    let mut content = String::new();
    writeln!(content, "/**").unwrap();
    if !config.header.is_empty() {
        for header_line in &config.header {
            writeln!(content, " * {}", header_line).unwrap();
        }
        writeln!(content, " *").unwrap();
    }
    content
}

fn md5(data: &str) -> String {
    let mut md5 = Md5::new();
    md5.input(data);
    hex::encode(md5.result())
}
<commit_msg>Use the correct source file for $normalizaiton files<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::apply_transforms::Programs;
use crate::config::{Config, ProjectConfig};
use crate::errors::BuildProjectError;
use common::FileKey;
use graphql_ir::{FragmentDefinition, NamedItem, OperationDefinition};
use graphql_text_printer::{
    print_fragment, print_full_operation, write_operation_with_graphqljs_formatting,
};
use graphql_transforms::MATCH_CONSTANTS;
use interner::StringKey;
use md5::{Digest, Md5};
use persist_query::persist;
use relay_codegen::{build_request_params, Printer};
use relay_typegen::{generate_fragment_type, generate_operation_type};
use signedsource::{sign_file, SIGNING_TOKEN};
use std::fmt::Write;

/// Represents a generated output artifact.
#[derive(Clone)]
pub struct Artifact {
    pub name: StringKey,
    pub content: String,
    pub hash: String,
    /// The source file responsible for generating this file.
    /// For example: `my_project/Component.react.js`
    pub source_file: FileKey,
}

pub async fn generate_artifacts(
    config: &Config,
    project_config: &ProjectConfig,
    programs: &Programs<'_>,
) -> Result<Vec<Artifact>, BuildProjectError> {
    let mut artifacts = Vec::new();
    let mut printer = Printer::default();
    for node in programs.normalization.operations() {
        if let Some(directive) = node
            .directives
            .named(MATCH_CONSTANTS.custom_module_directive_name)
        {
            // Generate normalization file for SplitOperation
            let name_arg = directive
                .arguments
                .named(MATCH_CONSTANTS.derived_from_arg)
                .unwrap();
            let source_name = name_arg
                .value
                .item
                .get_string_literal()
                .expect("Expected `derived_from` argument to be a literal string.");
            let source_node = programs
                .source
                .fragment(source_name)
                .expect("Expected the source document for the SplitOperation to exist.");
            let source_string = print_fragment(programs.source.schema(), &source_node);
            let hash = md5(&source_string);
            artifacts.push(generate_split_operation_artifact(
                &mut printer,
                config,
                programs,
                node,
                &hash,
                source_node.name.location.file(),
            ));
        } else {
            let source_node = programs.source.operation(node.name.item).unwrap();
            // TODO: Consider using the std::io::Write trait here to directly
            // write to the md5. Currently, this doesn't work as `write_operation`
            // expects a `std::fmt::Write`.
            // Same for fragment hashing below.
            let mut source_string = String::new();
            write_operation_with_graphqljs_formatting(
                programs.source.schema(),
                &source_node,
                &mut source_string,
            )
            .unwrap();
            let hash = md5(&source_string);
            artifacts.push(
                generate_normalization_artifact(
                    &mut printer,
                    config,
                    project_config,
                    programs,
                    node,
                    &hash,
                )
                .await?,
            );
        }
    }
    for node in programs.reader.fragments() {
        let source_node = programs.source.fragment(node.name.item).unwrap();
        // Same as for operation hashing above.
        let source_string = print_fragment(programs.source.schema(), &source_node);
        let hash = md5(&source_string);
        artifacts.push(generate_reader_artifact(
            &mut printer,
            config,
            programs,
            node,
            &hash,
        ));
    }

    Ok(artifacts)
}

async fn generate_normalization_artifact(
    printer: &mut Printer,
    config: &Config,
    project_config: &ProjectConfig,
    programs: &Programs<'_>,
    node: &OperationDefinition,
    source_hash: &str,
) -> Result<Artifact, BuildProjectError> {
    let name = node.name.item;
    let print_operation_node = programs
        .operation_text
        .operation(name)
        .expect("a query text operation should be generated for this operation");
    let text = print_full_operation(&programs.operation_text, print_operation_node);
    let mut request_parameters = build_request_params(&node);
    let mut operation_hash = None;
    if let Some(ref persist_config) = project_config.persist {
        operation_hash = Some(md5(&text));
        let id = persist(&text, &persist_config.url, &persist_config.params)
            .await
            .map_err(BuildProjectError::PersistError)?;
        request_parameters.id = Some(id);
    } else {
        request_parameters.text = Some(text.clone());
    };
    let reader_operation = programs
        .reader
        .operation(name)
        .expect("a reader fragment should be generated for this operation");
    let operation_fragment = FragmentDefinition {
        name: reader_operation.name,
        variable_definitions: reader_operation.variable_definitions.clone(),
        selections: reader_operation.selections.clone(),
        used_global_variables: Default::default(),
        directives: reader_operation.directives.clone(),
        type_condition: reader_operation.type_,
    };
    let mut content = get_content_start(config);
    writeln!(content, " * {}", SIGNING_TOKEN).unwrap();
    if let Some(operation_hash) = operation_hash {
        writeln!(content, " * @relayHash {}", operation_hash).unwrap();
    }
    writeln!(content, " * @flow").unwrap();
    writeln!(content, " * @lightSyntaxTransform").unwrap();
    writeln!(content, " * @nogrep").unwrap();
    if let Some(codegen_command) = &config.codegen_command {
        writeln!(content, " * @codegen-command: {}", codegen_command).unwrap();
    }
    writeln!(content, " */\n").unwrap();
    writeln!(content, "/* eslint-disable */\n").unwrap();
    writeln!(content, "'use strict';\n").unwrap();
    if let Some(id) = &request_parameters.id {
        writeln!(content, "// @relayRequestID {}\n", id).unwrap();
    }
    let typegen_node = programs
        .typegen
        .operation(name)
        .expect("a type fragment should be generated for this operation");
    writeln!(
        content,
        "/*::\nimport type {{ ConcreteRequest }} from 'relay-runtime';\n{}*/\n",
        generate_operation_type(typegen_node, programs.typegen.schema())
    )
    .unwrap();
    writeln!(content, "/*\n{}*/\n", text).unwrap();
    writeln!(
        content,
        "var node/*: ConcreteRequest*/ = {};\n",
        printer.print_request_deduped(
            programs.normalization.schema(),
            node,
            &operation_fragment,
            request_parameters
        )
    )
    .unwrap();
    writeln!(content, "if (__DEV__) {{").unwrap();
    writeln!(content, "  (node/*: any*/).hash = \"{}\";", source_hash).unwrap();
    writeln!(content, "}}\n").unwrap();
    writeln!(content, "module.exports = node;").unwrap();

    Ok(Artifact {
        name: node.name.item,
        content: sign_file(&content),
        hash: source_hash.to_string(),
        source_file: node.name.location.file(),
    })
}

fn generate_reader_artifact(
    printer: &mut Printer,
    config: &Config,
    programs: &Programs<'_>,
    node: &FragmentDefinition,
    hash: &str,
) -> Artifact {
    let name = node.name.item;
    let mut content = get_content_start(config);
    writeln!(content, " * {}", SIGNING_TOKEN).unwrap();
    writeln!(content, " * @flow").unwrap();
    writeln!(content, " * @lightSyntaxTransform").unwrap();
    writeln!(content, " * @nogrep").unwrap();
    if let Some(codegen_command) = &config.codegen_command {
        writeln!(content, " * @codegen-command: {}", codegen_command).unwrap();
    }
    writeln!(content, " */\n").unwrap();
    writeln!(content, "/* eslint-disable */\n").unwrap();
    writeln!(content, "'use strict';\n").unwrap();
    let typegen_node = programs
        .typegen
        .fragment(name)
        .expect("a type fragment should be generated for this fragment");
    writeln!(
        content,
        "/*::\nimport type {{ ReaderFragment }} from 'relay-runtime';\n{}*/\n",
        generate_fragment_type(typegen_node, programs.typegen.schema())
    )
    .unwrap();
    writeln!(
        content,
        "var node/*: ReaderFragment*/ = {};\n",
        printer.print_fragment_deduped(programs.normalization.schema(), node)
    )
    .unwrap();
    writeln!(content, "if (__DEV__) {{").unwrap();
    writeln!(content, "  (node/*: any*/).hash = \"{}\";", hash).unwrap();
    writeln!(content, "}}\n").unwrap();
    writeln!(content, "module.exports = node;").unwrap();

    Artifact {
        name,
        content: sign_file(&content),
        hash: hash.to_string(),
        source_file: node.name.location.file(),
    }
}

fn generate_split_operation_artifact(
    printer: &mut Printer,
    config: &Config,
    programs: &Programs<'_>,
    node: &OperationDefinition,
    hash: &str,
    source_file: FileKey,
) -> Artifact {
    let mut content = get_content_start(config);
    writeln!(content, " * {}", SIGNING_TOKEN).unwrap();
    writeln!(content, " * @flow").unwrap();
    writeln!(content, " * @lightSyntaxTransform").unwrap();
    writeln!(content, " * @nogrep").unwrap();
    if let Some(codegen_command) = &config.codegen_command {
        writeln!(content, " * @codegen-command: {}", codegen_command).unwrap();
    }
    writeln!(content, " */\n").unwrap();
    writeln!(content, "/* eslint-disable */\n").unwrap();
    writeln!(content, "'use strict';\n").unwrap();
    writeln!(
        content,
        "/*::\nimport type {{ NormalizationSplitOperation }} from 'relay-runtime';\n\n*/\n"
    )
    .unwrap();
    writeln!(
        content,
        "var node/*: NormalizationSplitOperation*/ = {};\n",
        printer.print_operation_deduped(programs.normalization.schema(), node)
    )
    .unwrap();
    writeln!(content, "if (__DEV__) {{").unwrap();
    writeln!(content, "  (node/*: any*/).hash = \"{}\";", hash).unwrap();
    writeln!(content, "}}\n").unwrap();
    writeln!(content, "module.exports = node;").unwrap();

    Artifact {
        name: node.name.item,
        content: sign_file(&content),
        hash: hash.to_string(),
        source_file,
    }
}

fn get_content_start(config: &Config) -> String {
    let mut content = String::new();
    writeln!(content, "/**").unwrap();
    if !config.header.is_empty() {
        for header_line in &config.header {
            writeln!(content, " * {}", header_line).unwrap();
        }
        writeln!(content, " *").unwrap();
    }
    content
}

fn md5(data: &str) -> String {
    let mut md5 = Md5::new();
    md5.input(data);
    hex::encode(md5.result())
}
<|endoftext|>


<commit_before>//! Dummy implementations that we emit along with an error.

use proc_macro2::Ident;
use proc_macro_error::append_dummy;
use quote::quote;

pub fn clap_struct(name: &Ident) {
    into_app(name);
    args(name);
    append_dummy(quote!( impl clap::Clap for #name {} ));
}

pub fn clap_enum(name: &Ident) {
    into_app(name);
    subcommand(name);
    append_dummy(quote!( impl clap::Clap for #name {} ));
}

pub fn into_app(name: &Ident) {
    append_dummy(quote! {
        impl clap::IntoApp for #name {
            fn into_app<'b>() -> clap::App<'b> {
                unimplemented!()
            }
            fn into_app_for_update<'b>() -> clap::App<'b> {
                unimplemented!()
            }
        }
    });
}

pub fn from_arg_matches(name: &Ident) {
    append_dummy(quote! {
        impl clap::FromArgMatches for #name {
            fn from_arg_matches(_m: &clap::ArgMatches) -> Option<Self> {
                unimplemented!()
            }
            fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) {
                unimplemented!()
            }
        }
    });
}

pub fn subcommand(name: &Ident) {
    from_arg_matches(name);
    append_dummy(quote! {
        impl clap::Subcommand for #name {
            fn augment_subcommands(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
            fn augment_subcommands_for_update(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
            fn has_subcommand(name: &str) -> bool {
                unimplemented!()
            }
        }
    });
}

pub fn args(name: &Ident) {
    from_arg_matches(name);
    append_dummy(quote! {
        impl clap::Args for #name {
            fn augment_args(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
            fn augment_args_for_update(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
        }
    });
}

pub fn arg_enum(name: &Ident) {
    append_dummy(quote! {
        impl clap::ArgEnum for #name {
            fn arg_values() -> Vec<clap::ArgValue<'static>> {
                unimplemented!()
            }
            fn from_str(_input: &str, _case_insensitive: bool) -> Result<Self, String> {
                unimplemented!()
            }
            fn arg_value(&self) -> Option<clap::ArgValue<'static>> {
                unimplemented!()
            }
        }
    })
}
<commit_msg>fix dummies<commit_after>//! Dummy implementations that we emit along with an error.

use proc_macro2::Ident;
use proc_macro_error::append_dummy;
use quote::quote;

pub fn clap_struct(name: &Ident) {
    into_app(name);
    args(name);
    append_dummy(quote!( impl clap::Clap for #name {} ));
}

pub fn clap_enum(name: &Ident) {
    into_app(name);
    subcommand(name);
    append_dummy(quote!( impl clap::Clap for #name {} ));
}

pub fn into_app(name: &Ident) {
    append_dummy(quote! {
        impl clap::IntoApp for #name {
            fn into_app<'b>() -> clap::App<'b> {
                unimplemented!()
            }
            fn into_app_for_update<'b>() -> clap::App<'b> {
                unimplemented!()
            }
        }
    });
}

pub fn from_arg_matches(name: &Ident) {
    append_dummy(quote! {
        impl clap::FromArgMatches for #name {
            fn from_arg_matches(_m: &clap::ArgMatches) -> Option<Self> {
                unimplemented!()
            }
            fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) {
                unimplemented!()
            }
        }
    });
}

pub fn subcommand(name: &Ident) {
    from_arg_matches(name);
    append_dummy(quote! {
        impl clap::Subcommand for #name {
            fn augment_subcommands(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
            fn augment_subcommands_for_update(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
            fn has_subcommand(name: &str) -> bool {
                unimplemented!()
            }
        }
    });
}

pub fn args(name: &Ident) {
    from_arg_matches(name);
    append_dummy(quote! {
        impl clap::Args for #name {
            fn augment_args(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
            fn augment_args_for_update(_app: clap::App<'_>) -> clap::App<'_> {
                unimplemented!()
            }
        }
    });
}

pub fn arg_enum(name: &Ident) {
    append_dummy(quote! {
        impl clap::ArgEnum for #name {
            fn value_variants<'a>() -> &'a [Self]{
                unimplemented!()
            }
            fn from_str(_input: &str, _case_insensitive: bool) -> Result<Self, String> {
                unimplemented!()
            }
            fn arg_value<'a>(&self) -> Option<clap::ArgValue<'a>>{
                unimplemented!()
            }
        }
    })
}
<|endoftext|>


<commit_before>// Copyright 2015-2016 Deyan Ginev. See the LICENSE
// file at the top-level directory of this distribution.
//
// Licensed under the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
extern crate cortex;
extern crate postgres;

// use cortex::backend::*;

#[test]
fn add_corpus_route() {

}<commit_msg>remove empty frontend test<commit_after><|endoftext|>


<commit_before>use std::io;
use std::io::BufRead;
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::fmt::{Debug, Formatter, Error};

fn stock_maximize(stock_values: &Vec<i64>) -> i64 {
    let mut money = 0i64;
    let mut current_max = *stock_values.last().unwrap();
    for i in (0..stock_values.len()).rev() {
        if stock_values[i] <= current_max {
            money += current_max - stock_values[i];
        } else {
            current_max = stock_values[i];
        }
    }
    money
}

#[derive(Hash, Clone)]
struct StockPosition {
    stock_count: i32,
    money: i32,
}
impl Debug for StockPosition {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(f, "[stocks:{} money:{}]",
            self.stock_count, self.money)
    }
}

fn stock_maximize2(stock_values: &Vec<i32>) -> i32 {
    let mut stock_positions :HashMap<i32, StockPosition> = HashMap::new();
    stock_positions.insert(0, StockPosition {stock_count: 0, money: 0});
    for stock_value in stock_values.iter() {
        let mut new_stock_position :HashMap<i32, StockPosition> = HashMap::new();
        for position in stock_positions.values() {
            let position_do_nothing = position.clone();
            insert_if_more_money(&mut new_stock_position, position_do_nothing);

            let mut position_buy = position.clone();
            position_buy.stock_count += 1;
            position_buy.money -= *stock_value;
            insert_if_more_money(&mut new_stock_position, position_buy);

            let mut position_sell = position.clone();
            position_sell.money += position.stock_count * stock_value;
            position_sell.stock_count = 0;
            insert_if_more_money(&mut new_stock_position, position_sell);
        }
        //println!("stock value:{} positions:{:?}", stock_value, new_stock_position);
        stock_positions = new_stock_position;
    }
    stock_positions.values().map(|pos| pos.money).max().unwrap()
}

fn insert_if_more_money(
    stock_positions: &mut HashMap<i32, StockPosition>,
    position: StockPosition) {
    match stock_positions.entry(position.stock_count) {
        Vacant(entry) => {entry.insert(position); ()},
        Occupied(mut entry) => {
            if entry.get().money < position.money {
                entry.insert(position);
            }
        },
    }
}

#[allow(dead_code)]
fn main() {
    let stdin = io::stdin();
    let mut lines = stdin.lock().lines();
    let first_line = lines.next().unwrap().unwrap();
    let t = first_line.trim().parse::<usize>().unwrap();
    let mut lines_enum = lines.take(t*2);
    let mut results = Vec::<i64>::with_capacity(t);
    while let Some(line_count) = lines_enum.next() {
        let count = line_count.unwrap().trim().parse::<usize>().unwrap();
        let line_values = lines_enum.next().unwrap().unwrap();
        let values: Vec<i64> = line_values.trim().split(' ')
            .map(|value| value.parse::<i64>().unwrap())
            .take(count)
            .collect();
        results.push(stock_maximize(&values));
    }

    for result in results.iter() {
        println!("{}", result);
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn sample1() {
        assert_eq!(0, super::stock_maximize(&vec![5, 3, 2]));
    }

    #[test]
    fn sample2() {
        assert_eq!(197, super::stock_maximize(&vec![1, 2, 100]));
    }

    #[test]
    fn sample3() {
        assert_eq!(3, super::stock_maximize(&vec![1, 3, 1, 2]));
    }

    //&(1..250).collect()
    #[test]
    fn big() {
        super::stock_maximize(&(1..40000).collect());
    }
}<commit_msg>remove old slow solution for stock maximize<commit_after>use std::io;
use std::io::BufRead;

fn stock_maximize(stock_values: &Vec<i64>) -> i64 {
    let mut money = 0i64;
    let mut current_max = *stock_values.last().unwrap();
    for i in (0..stock_values.len()).rev() {
        if stock_values[i] <= current_max {
            money += current_max - stock_values[i];
        } else {
            current_max = stock_values[i];
        }
    }
    money
}

#[allow(dead_code)]
fn main() {
    let stdin = io::stdin();
    let mut lines = stdin.lock().lines();
    let first_line = lines.next().unwrap().unwrap();
    let t = first_line.trim().parse::<usize>().unwrap();
    let mut lines_enum = lines.take(t*2);
    let mut results = Vec::<i64>::with_capacity(t);
    while let Some(line_count) = lines_enum.next() {
        let count = line_count.unwrap().trim().parse::<usize>().unwrap();
        let line_values = lines_enum.next().unwrap().unwrap();
        let values: Vec<i64> = line_values.trim().split(' ')
            .map(|value| value.parse::<i64>().unwrap())
            .take(count)
            .collect();
        results.push(stock_maximize(&values));
    }

    for result in results.iter() {
        println!("{}", result);
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn sample1() {
        assert_eq!(0, super::stock_maximize(&vec![5, 3, 2]));
    }

    #[test]
    fn sample2() {
        assert_eq!(197, super::stock_maximize(&vec![1, 2, 100]));
    }

    #[test]
    fn sample3() {
        assert_eq!(3, super::stock_maximize(&vec![1, 3, 1, 2]));
    }

    //&(1..250).collect()
    #[test]
    fn big() {
        super::stock_maximize(&(1..40000).collect());
    }
}<|endoftext|>


<commit_before>
use std::io;

use parser::*;
use scope::*;
use processing::*;
use output::*;

impl ElementOpsStreamWriter for DefaultOutputWriterJs {

    fn write_op_element_open<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, element_tag: &str, element_key: &str, is_void: bool, props: PropIter, events: EventIter, binding: BindingIter) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        if !is_void {
            write!(w, "IncrementalDOM.elementOpen(\"{}\", ", element_tag)?;
        } else {
            write!(w, "IncrementalDOM.elementVoid(\"{}\", ", element_tag)?;
        };

        let path_expr = ctx.join_path_as_expr_with(Some("."), element_key);
        self.write_expr(w, doc, ctx, bindings, &path_expr)?;
        write!(w, ", [")?;

        let mut props: PropVec = props.into_iter().map(|p| (p.0.to_owned(), p.1.map(|s| s.to_owned()))).collect();
        props.insert(0, ("key".into(), Some(path_expr.clone())));

        let mut first_item = true;
        for ref prop in props.iter() {
            if let Some(ref expr) = prop.1 {
                if !first_item { write!(w, ", ")?; }
                first_item = false;
                write!(w, "\"{}\", ", &prop.0)?;
                if let &ExprValue::SymbolReference(ref sym) = expr {
                    if sym.is_bool() {
                        self.write_expr(w, doc, ctx, bindings, expr)?;
                        write!(w, " ? \"{}\" : null", &prop.0)?;
                        continue;
                    };
                };
                self.write_expr(w, doc, ctx, bindings, expr)?;
            };
        }

        writeln!(w, "]);")?;

        // Handle special case
        if element_tag == "input" {
            for value_binding in binding {
                if let &Some((_, ref sym)) = value_binding {
                    if let &SymbolReferenceType::InitialValue(box ref initial, box ref element) = sym.sym_ref() {
                        let expr = ExprValue::SymbolReference(element.to_owned());
                        self.write_expr(w, doc, ctx, bindings, &expr)?;
                        write!(w, " = ")?;
                        let expr = ExprValue::SymbolReference(initial.to_owned());
                        self.write_expr(w, doc, ctx, bindings, &expr)?;
                        writeln!(w, ";")?;
                    };
                };
            }
        };

        Ok(())
    }

    fn write_op_element_close(&mut self, w: &mut io::Write, _doc: &Document, ctx: &mut Context, _bindings: &BindingContext, element_tag: &str) -> Result {
        writeln!(w, "IncrementalDOM.elementClose(\"{}\");", element_tag)?;
        Ok(())
    }

    fn write_op_element_start_block<PropIter: IntoIterator<Item = Prop>>(&mut self, _w: &mut io::Write, __doc: &Document, ctx: &mut Context, _bindings: &BindingContext, _block_id: &str, _props: PropIter) -> Result {
        Ok(())
    }

    fn write_op_element_end_block(&mut self, _w: &mut io::Write, __doc: &Document, ctx: &mut Context, _bindings: &BindingContext, _block_id: &str) -> Result {
        Ok(())
    }

    fn write_op_element_map_collection_to_block(&mut self, w: &mut io::Write, __doc: &Document, ctx: &mut Context, _bindings: &BindingContext, _coll_expr: &ExprValue, block_id: &str) -> Result {
        write!(w, "(")?;
        // let binding = BindingType::LoopIndexBinding;
        writeln!(w, ").forEach(__{});", block_id)?;
        Ok(())
    }

    fn write_op_element_instance_component<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, element_tag: &str, element_key: &str, is_void: bool, props: PropIter, events: EventIter, binding: BindingIter) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        let instance_key = ctx.join_path_as_expr_with(Some("."), element_key);
        self.render_component(w, doc, ctx, bindings, Some("div"), element_tag, InstanceKey::Dynamic(&instance_key), is_void, props, events, binding, None)
    }

    fn write_op_element_value(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, expr: &ExprValue, _element_key: &str) -> Result {
        write!(w, "IncrementalDOM.text(")?;
        self.write_expr(w, doc, ctx, bindings, expr)?;
        writeln!(w, ");")?;
        Ok(())
    }
}

impl ElementOpsUtilWriter for DefaultOutputWriterJs {
    fn render_component<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, enclosing_tag: Option<&str>, component_ty: &str, instance_key: InstanceKey, _is_void: bool, props: PropIter, _events: EventIter, _binding: BindingIter, _lens_item: Option<LensItemType<'a>>) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        let instance_key = instance_key.as_expr();
        write!(w, "component_{}(", component_ty)?;
        self.write_expr(w, doc, ctx, bindings, &instance_key)?;
        write!(w, ", {{")?;
        let mut first_item = true;
        for ref prop in props.into_iter() {
            if let Some(ref expr) = prop.1 {
                if !first_item { write!(w, ", ")?; }
                first_item = false;
                write!(w, "\"{}\": ", &prop.0)?;
                self.write_expr(w, doc, ctx, bindings, &expr)?;
            };
        }
        writeln!(w, "}}, store);")?;
        Ok(())
    }

    fn write_map_collection_to_component<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, coll_item_key: &str, coll_expr: &ExprValue, enclosing_tag: Option<&str>, component_ty: &str, instance_key: InstanceKey, props: PropIter, events: EventIter, binding: BindingIter) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        let instance_key = instance_key.as_static_string();
        let path_expr = ctx.join_path_as_expr_with(Some("."), &instance_key);

        write!(w, "(")?;
        self.write_expr(w, doc, ctx, bindings, coll_expr)?;
        write!(w, ").forEach(function(item, idx) {{ component_{}(", component_ty)?;
        self.write_expr(w, doc, ctx, bindings, &path_expr)?;
        write!(w, " + \".\" + idx, {{")?;

        let mut first_item = true;
        for ref prop in props.into_iter() {
            if let Some(ref expr) = prop.1 {
                if !first_item { write!(w, ", ")?; }
                first_item = false;
                write!(w, "\"{}\": ", &prop.0)?;
                self.write_expr(w, doc, ctx, bindings, &expr)?;
            };
        }
        writeln!(w, "}}); }});")?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str;
    use std::iter::empty;
    use scope::context::*;
    use scope::bindings::*;
    use output::writers::*;

    fn create_document<'a>(template: &'a Template) -> Document {
        let mut ctx = Context::default();
        let mut bindings = BindingContext::default();
        let mut processing = ProcessDocument::from_template(&template);
        assert!(processing.process_document(&mut ctx, &mut bindings).is_ok());
        processing.into()
    }

    #[test]
    pub fn test_output_stream_writers_js_ops1() {
        let template = Template::new(vec![]);
        let doc = create_document(&template);

        let mut ctx = Context::default();
        ctx.append_path_str("prefix");
        let bindings = BindingContext::default();

        let mut writer = DefaultOutputWriterJs::default();

        let mut s: Vec<u8> = Default::default();
        let key = "key".to_owned();
        assert!(
            writer.write_op_element_open(&mut s, &doc, &mut ctx, &bindings, "span", &key, false, empty(), empty(), empty()).is_ok() &&
            writer.write_op_element_close(&mut s, &doc, &mut ctx, &bindings, "span").is_ok()
        );
        // assert_eq!(str::from_utf8(&s), Ok("IncrementalDOM.elementOpen(\"span\", [\"prefix\", \"key\"].join(\".\"));\nIncrementalDOM.elementClose(\"span\");\n".into()));
        assert_eq!(str::from_utf8(&s), Ok(indoc![r#"
            IncrementalDOM.elementOpen("span", ["prefix", "key"].join("."));
            IncrementalDOM.elementClose("span");
        "#]));
    }
}<commit_msg>Work around IncrementalDOM handling of certain attributes like `checked`. Use `null` as `false` and `true` as `true`.<commit_after>
use std::io;

use parser::*;
use scope::*;
use processing::*;
use output::*;

impl ElementOpsStreamWriter for DefaultOutputWriterJs {

    fn write_op_element_open<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, element_tag: &str, element_key: &str, is_void: bool, props: PropIter, events: EventIter, binding: BindingIter) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        if !is_void {
            write!(w, "IncrementalDOM.elementOpen(\"{}\", ", element_tag)?;
        } else {
            write!(w, "IncrementalDOM.elementVoid(\"{}\", ", element_tag)?;
        };

        let path_expr = ctx.join_path_as_expr_with(Some("."), element_key);
        self.write_expr(w, doc, ctx, bindings, &path_expr)?;
        write!(w, ", [")?;

        let mut props: PropVec = props.into_iter().map(|p| (p.0.to_owned(), p.1.map(|s| s.to_owned()))).collect();
        props.insert(0, ("key".into(), Some(path_expr.clone())));

        let mut first_item = true;
        for ref prop in props.iter() {
            if let Some(ref expr) = prop.1 {
                if !first_item { write!(w, ", ")?; }
                first_item = false;
                write!(w, "\"{}\", ", &prop.0)?;
                if let &ExprValue::SymbolReference(ref sym) = expr {
                    if sym.is_bool() {
                        self.write_expr(w, doc, ctx, bindings, expr)?;
                        // write!(w, " ? \"{}\" : null", &prop.0)?;
                        write!(w, " ? true : null")?;
                        continue;
                    };
                };
                self.write_expr(w, doc, ctx, bindings, expr)?;
            };
        }

        writeln!(w, "]);")?;

        // Handle special case
        if element_tag == "input" {
            for value_binding in binding {
                if let &Some((_, ref sym)) = value_binding {
                    if let &SymbolReferenceType::InitialValue(box ref initial, box ref element) = sym.sym_ref() {
                        let expr = ExprValue::SymbolReference(element.to_owned());
                        self.write_expr(w, doc, ctx, bindings, &expr)?;
                        write!(w, " = ")?;
                        let expr = ExprValue::SymbolReference(initial.to_owned());
                        self.write_expr(w, doc, ctx, bindings, &expr)?;
                        writeln!(w, ";")?;
                    };
                };
            }
        };

        Ok(())
    }

    fn write_op_element_close(&mut self, w: &mut io::Write, _doc: &Document, ctx: &mut Context, _bindings: &BindingContext, element_tag: &str) -> Result {
        writeln!(w, "IncrementalDOM.elementClose(\"{}\");", element_tag)?;
        Ok(())
    }

    fn write_op_element_start_block<PropIter: IntoIterator<Item = Prop>>(&mut self, _w: &mut io::Write, __doc: &Document, ctx: &mut Context, _bindings: &BindingContext, _block_id: &str, _props: PropIter) -> Result {
        Ok(())
    }

    fn write_op_element_end_block(&mut self, _w: &mut io::Write, __doc: &Document, ctx: &mut Context, _bindings: &BindingContext, _block_id: &str) -> Result {
        Ok(())
    }

    fn write_op_element_map_collection_to_block(&mut self, w: &mut io::Write, __doc: &Document, ctx: &mut Context, _bindings: &BindingContext, _coll_expr: &ExprValue, block_id: &str) -> Result {
        write!(w, "(")?;
        // let binding = BindingType::LoopIndexBinding;
        writeln!(w, ").forEach(__{});", block_id)?;
        Ok(())
    }

    fn write_op_element_instance_component<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, element_tag: &str, element_key: &str, is_void: bool, props: PropIter, events: EventIter, binding: BindingIter) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        let instance_key = ctx.join_path_as_expr_with(Some("."), element_key);
        self.render_component(w, doc, ctx, bindings, Some("div"), element_tag, InstanceKey::Dynamic(&instance_key), is_void, props, events, binding, None)
    }

    fn write_op_element_value(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, expr: &ExprValue, _element_key: &str) -> Result {
        write!(w, "IncrementalDOM.text(")?;
        self.write_expr(w, doc, ctx, bindings, expr)?;
        writeln!(w, ");")?;
        Ok(())
    }
}

impl ElementOpsUtilWriter for DefaultOutputWriterJs {
    fn render_component<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, enclosing_tag: Option<&str>, component_ty: &str, instance_key: InstanceKey, _is_void: bool, props: PropIter, _events: EventIter, _binding: BindingIter, _lens_item: Option<LensItemType<'a>>) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        let instance_key = instance_key.as_expr();
        write!(w, "component_{}(", component_ty)?;
        self.write_expr(w, doc, ctx, bindings, &instance_key)?;
        write!(w, ", {{")?;
        let mut first_item = true;
        for ref prop in props.into_iter() {
            if let Some(ref expr) = prop.1 {
                if !first_item { write!(w, ", ")?; }
                first_item = false;
                write!(w, "\"{}\": ", &prop.0)?;
                self.write_expr(w, doc, ctx, bindings, &expr)?;
            };
        }
        writeln!(w, "}}, store);")?;
        Ok(())
    }

    fn write_map_collection_to_component<'a, PropIter, EventIter, BindingIter>(&mut self, w: &mut io::Write, doc: &Document, ctx: &mut Context, bindings: &BindingContext, coll_item_key: &str, coll_expr: &ExprValue, enclosing_tag: Option<&str>, component_ty: &str, instance_key: InstanceKey, props: PropIter, events: EventIter, binding: BindingIter) -> Result
      where PropIter : IntoIterator<Item = ActualPropRef<'a>>, EventIter: IntoIterator<Item = &'a EventHandler>, BindingIter: IntoIterator<Item = &'a ElementValueBinding>
    {
        let instance_key = instance_key.as_static_string();
        let path_expr = ctx.join_path_as_expr_with(Some("."), &instance_key);

        write!(w, "(")?;
        self.write_expr(w, doc, ctx, bindings, coll_expr)?;
        write!(w, ").forEach(function(item, idx) {{ component_{}(", component_ty)?;
        self.write_expr(w, doc, ctx, bindings, &path_expr)?;
        write!(w, " + \".\" + idx, {{")?;

        let mut first_item = true;
        for ref prop in props.into_iter() {
            if let Some(ref expr) = prop.1 {
                if !first_item { write!(w, ", ")?; }
                first_item = false;
                write!(w, "\"{}\": ", &prop.0)?;
                self.write_expr(w, doc, ctx, bindings, &expr)?;
            };
        }
        writeln!(w, "}}); }});")?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::str;
    use std::iter::empty;
    use scope::context::*;
    use scope::bindings::*;
    use output::writers::*;

    fn create_document<'a>(template: &'a Template) -> Document {
        let mut ctx = Context::default();
        let mut bindings = BindingContext::default();
        let mut processing = ProcessDocument::from_template(&template);
        assert!(processing.process_document(&mut ctx, &mut bindings).is_ok());
        processing.into()
    }

    #[test]
    pub fn test_output_stream_writers_js_ops1() {
        let template = Template::new(vec![]);
        let doc = create_document(&template);

        let mut ctx = Context::default();
        ctx.append_path_str("prefix");
        let bindings = BindingContext::default();

        let mut writer = DefaultOutputWriterJs::default();

        let mut s: Vec<u8> = Default::default();
        let key = "key".to_owned();
        assert!(
            writer.write_op_element_open(&mut s, &doc, &mut ctx, &bindings, "span", &key, false, empty(), empty(), empty()).is_ok() &&
            writer.write_op_element_close(&mut s, &doc, &mut ctx, &bindings, "span").is_ok()
        );
        // assert_eq!(str::from_utf8(&s), Ok("IncrementalDOM.elementOpen(\"span\", [\"prefix\", \"key\"].join(\".\"));\nIncrementalDOM.elementClose(\"span\");\n".into()));
        assert_eq!(str::from_utf8(&s), Ok(indoc![r#"
            IncrementalDOM.elementOpen("span", ["prefix", "key"].join("."));
            IncrementalDOM.elementClose("span");
        "#]));
    }
}<|endoftext|>


<commit_before>
use std::cmp;

type Rgba = [u8; 4];

fn minf(a: f32, b: f32) -> f32 { if a < b { a } else { b } }
fn maxf(a: f32, b: f32) -> f32 { if a > b { a } else { b } }
fn saturate(a: f32) -> f32 { maxf(minf(a, 1.0), 0.0) }
fn fnorm(a: u8) -> f32 { a as f32 / 255.0 }
fn bnorm(a: f32) -> u8 { (saturate(a) * 255.0) as u8 }

/*type Hsva = [u8; 4];

fn to_hsva(rgba: &Rgba) -> Hsva {
	let (r, g, b) = (fnorm(rgba[0]), fnorm(rgba[1]), fnorm(rgba[2]));
	let max = maxf(maxf(r , g), b);
	let min = minf(minf(r , g), b);
	let c = max - min;
	let h =
		if c == 0.0 {
			0.0
		} else {
			(if max == r {
				(g - b) / c
			} else if max == g {
				(b - r) / c + 2.0
			} else {
				(r - g) / c + 4.0
			}) / 6.0
		};

	let s = if max == 0.0 { 0.0 } else { c / max };
	[bnorm(h), bnorm(s), bnorm(max), rgba[3]]
}

fn to_rgba(hsva: &Hsva) -> Rgba {
	let v = fnorm(hsva[2]);
	let c = v * fnorm(hsva[3]);
	let h = fnorm(hsva[0]) * 6.0;
	let x = c * (1.0 - (h % 2.0 - 1.0).abs());
	let rgb1 =
		if h < 1.0 {
			(c, x, 0.0)
		} else if h < 2.0 {
			(x, c, 0.0)
		} else if h < 3.0 {
			(0.0, c, x)
		} else if h < 4.0 {
			(0.0, x, c)
		} else if h < 5.0 {
			(x, 0.0, c)
		} else {
			(c, 0.0, x)
		};
	let m = v - c;
	[bnorm(rgb1.0 + m),
	bnorm(rgb1.1 + m),
	bnorm(rgb1.2 + m),
	hsva[3]]
}*/

fn bc1_dist(a: &Rgba, b: &Rgba) -> u32 {
	fn bc1_dist_one(a: u8, b: u8) -> u32 {
		let x = a as i32 - b as i32;
		(x * x) as u32
	}
	bc1_dist_one(a[0], b[0]) +
	bc1_dist_one(a[1], b[1]) +
	bc1_dist_one(a[2], b[2])
}

fn bc1_total_dist(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut sum = 0u32;
	let mut max = 0u32;
	for pix in pixels.into_iter().rev() {
		let e = (0..4).into_iter().map(|x| bc1_dist(&table[x], pix)).min().unwrap();
		sum += e;
		max = cmp::max(max, e);
	}
	sum + max * 16
}

fn bc1_build_entry(min: &Rgba, max: &Rgba, index: usize) -> Rgba {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[((coefs.1 * min[0] as u16 + coefs.0 * max[0] as u16) / 3) as u8,
	 ((coefs.1 * min[1] as u16 + coefs.0 * max[1] as u16) / 3) as u8,
	 ((coefs.1 * min[2] as u16 + coefs.0 * max[2] as u16) / 3) as u8,
	 ((coefs.1 * min[3] as u16 + coefs.0 * max[3] as u16) / 3) as u8]
}

fn bc1_build_table(min: &Rgba, max: &Rgba) -> [Rgba; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_build_entry(&min, &max, i);
	}
	interps
}

fn bc1_minmax(pixels: &[Rgba; 16]) -> (Rgba, Rgba) {
	let mut min = [u8::max_value(); 4];
	let mut max = [0u8; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i]);
			max[i] = cmp::max(max[i], p[i]);
		}
	}
	(min, max)
}

fn bc1_extrapolate_endpoint(a: &Rgba, b: &Rgba) -> [u8; 4] {
	let mut diff = *a;
	for i in 0..3 {
		let f = fnorm(diff[i]) - ((fnorm(a[i]) - fnorm(b[i])) * 1.5);
		diff[i] = bnorm(f);
	}
	diff
}

fn bc1_sort_endpoints(e: (Rgba, Rgba)) -> (Rgba, Rgba) {
	if bc1_encode_endpoint(&e.0) > bc1_encode_endpoint(&e.1) {
		(e.1, e.0)
	} else {
		(e.0, e.1)
	}
}

fn bc1_build_endpoints(pixels: &[Rgba; 16], quality: u8) -> Vec<(Rgba, Rgba)> {
	let mut out = Vec::new();

	out.push(bc1_minmax(pixels));

	if quality > 0 {
		for i in 0..16 {
			for j in i..16 {
				if i != j {
					let a = pixels[i];
					let b = pixels[j];
					out.push(bc1_sort_endpoints((a, b)));
					if quality > 1 {
						let endpoints = (bc1_extrapolate_endpoint(&a, &b), bc1_extrapolate_endpoint(&b, &a));
						out.push(bc1_sort_endpoints(endpoints));
					}
				}
			}
		}
	}

	out
}

fn bc1_encode_endpoint(pix: &Rgba) -> u16 {
	((pix[0] as u16 >> 3) << 11) |
	((pix[1] as u16 >> 2) << 5) |
	(pix[2] as u16 >> 3)
}

fn bc1_encode_pixel(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| bc1_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn bc1_encode_block(pixels: &[Rgba; 16], quality: u8) -> (u64, u32) {
	/*let (min, max) = bc1_build_endpoints(&pixels)[0];
	let table = bc1_build_table(&min, &max);*/

	let ((min, max), table) = bc1_build_endpoints(pixels, quality).iter()
		.map(|pts| (*pts, bc1_build_table(&pts.0, &pts.1)))
		.min_by_key(|tbl| bc1_total_dist(&tbl.1, pixels)).unwrap();

	let dist = bc1_total_dist(&table, pixels);

	let min = bc1_encode_endpoint(&min) as u64;
	let max = bc1_encode_endpoint(&max) as u64;

	let encoded = bc1_encode_pixel(&table, &pixels) as u64;

	((encoded << 32) | (min << 16) | max, dist)
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize), quality: u8) -> Result<Vec<u8>, ()> {
	if !(size.0 % 4 == 0 && size.1 % 4 == 0) {
		return Err(());
	}

    let mut out = Vec::with_capacity(image.len() / 8);
    let mut total_dist = 0u32;

    let block_size = 4;
    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let (mut encoded, dist) = bc1_encode_block(&pixels, quality);
   			total_dist += dist;

   			for _ in 0..8 {
   				out.push((encoded & 0xFF) as u8);
   				encoded >>= 8;
   			}
		}
	}
	//println!("per-pixel dist = {:?}", total_dist / (size.0 * size.1) as u32);
	Ok(out)
}
<commit_msg>Compressing trivial blocks is now much faster (35% improvement on example texture)<commit_after>
use std::cmp;

type Rgba = [u8; 4];

fn minf(a: f32, b: f32) -> f32 { if a < b { a } else { b } }
fn maxf(a: f32, b: f32) -> f32 { if a > b { a } else { b } }
fn saturate(a: f32) -> f32 { maxf(minf(a, 1.0), 0.0) }
fn fnorm(a: u8) -> f32 { a as f32 / 255.0 }
fn bnorm(a: f32) -> u8 { (saturate(a) * 255.0) as u8 }

/*type Hsva = [u8; 4];

fn to_hsva(rgba: &Rgba) -> Hsva {
	let (r, g, b) = (fnorm(rgba[0]), fnorm(rgba[1]), fnorm(rgba[2]));
	let max = maxf(maxf(r , g), b);
	let min = minf(minf(r , g), b);
	let c = max - min;
	let h =
		if c == 0.0 {
			0.0
		} else {
			(if max == r {
				(g - b) / c
			} else if max == g {
				(b - r) / c + 2.0
			} else {
				(r - g) / c + 4.0
			}) / 6.0
		};

	let s = if max == 0.0 { 0.0 } else { c / max };
	[bnorm(h), bnorm(s), bnorm(max), rgba[3]]
}

fn to_rgba(hsva: &Hsva) -> Rgba {
	let v = fnorm(hsva[2]);
	let c = v * fnorm(hsva[3]);
	let h = fnorm(hsva[0]) * 6.0;
	let x = c * (1.0 - (h % 2.0 - 1.0).abs());
	let rgb1 =
		if h < 1.0 {
			(c, x, 0.0)
		} else if h < 2.0 {
			(x, c, 0.0)
		} else if h < 3.0 {
			(0.0, c, x)
		} else if h < 4.0 {
			(0.0, x, c)
		} else if h < 5.0 {
			(x, 0.0, c)
		} else {
			(c, 0.0, x)
		};
	let m = v - c;
	[bnorm(rgb1.0 + m),
	bnorm(rgb1.1 + m),
	bnorm(rgb1.2 + m),
	hsva[3]]
}*/

fn bc1_dist(a: &Rgba, b: &Rgba) -> u32 {
	fn bc1_dist_one(a: u8, b: u8) -> u32 {
		let x = a as i32 - b as i32;
		(x * x) as u32
	}
	bc1_dist_one(a[0], b[0]) +
	bc1_dist_one(a[1], b[1]) +
	bc1_dist_one(a[2], b[2])
}

fn bc1_total_dist(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut sum = 0u32;
	let mut max = 0u32;
	for pix in pixels.into_iter().rev() {
		let e = (0..4).into_iter().map(|x| bc1_dist(&table[x], pix)).min().unwrap();
		sum += e;
		max = cmp::max(max, e);
	}
	sum + max * 16
}

fn bc1_build_entry(min: &Rgba, max: &Rgba, index: usize) -> Rgba {
	let coefs = match index {
		0x0 => (3, 0),
		0x1 => (0, 3),
		0x2 => (2 ,1),
		_ => (1, 2)
	};
	[((coefs.1 * min[0] as u16 + coefs.0 * max[0] as u16) / 3) as u8,
	 ((coefs.1 * min[1] as u16 + coefs.0 * max[1] as u16) / 3) as u8,
	 ((coefs.1 * min[2] as u16 + coefs.0 * max[2] as u16) / 3) as u8,
	 ((coefs.1 * min[3] as u16 + coefs.0 * max[3] as u16) / 3) as u8]
}

fn bc1_build_table(min: &Rgba, max: &Rgba) -> [Rgba; 4] {
	let mut interps = [[0u8; 4]; 4];
	for i in 0..4 {
		interps[i] = bc1_build_entry(&min, &max, i);
	}
	interps
}

fn bc1_minmax(pixels: &[Rgba; 16]) -> (Rgba, Rgba) {
	let mut min = [u8::max_value(); 4];
	let mut max = [0u8; 4];
	for p in pixels.into_iter() {
		for i in 0..4 {
			min[i] = cmp::min(min[i], p[i]);
			max[i] = cmp::max(max[i], p[i]);
		}
	}
	(min, max)
}

fn bc1_extrapolate_endpoint(a: &Rgba, b: &Rgba) -> [u8; 4] {
	let mut diff = *a;
	for i in 0..3 {
		let f = fnorm(diff[i]) - ((fnorm(a[i]) - fnorm(b[i])) * 1.5);
		diff[i] = bnorm(f);
	}
	diff
}

fn bc1_sort_endpoints(e: (Rgba, Rgba)) -> (Rgba, Rgba) {
	if bc1_encode_endpoint(&e.0) > bc1_encode_endpoint(&e.1) {
		(e.1, e.0)
	} else {
		(e.0, e.1)
	}
}

/*fn bc1_average(pixels: &[Rgba; 16]) -> Rgba {
	let mut avg = [0u16; 4];
	for i in 1..16 {
		for c in 0..4 {
			avg[c] += pixels[i][c] as u16;
		}
	}
	[(avg[0] / 16) as u8, (avg[1] / 16) as u8, (avg[2] / 16) as u8, (avg[3] / 16) as u8]
}*/

fn bc1_build_endpoints(pixels: &[Rgba; 16], quality: u8) -> Vec<(Rgba, Rgba)> {
	let (min, max) = bc1_minmax(pixels);

	let mut out = Vec::new();
	out.push((min, max));

	if quality > 0 && min != max {
		for i in 0..16 {
			for j in i..16 {
				if i != j {
					let a = pixels[i];
					let b = pixels[j];
					out.push(bc1_sort_endpoints((a, b)));
					if quality > 1 {
						let endpoints = (bc1_extrapolate_endpoint(&a, &b), bc1_extrapolate_endpoint(&b, &a));
						out.push(bc1_sort_endpoints(endpoints));
					}

				}
			}
		}
	}
	out
}

fn bc1_encode_endpoint(pix: &Rgba) -> u16 {
	((pix[0] as u16 >> 3) << 11) |
	((pix[1] as u16 >> 2) << 5) |
	(pix[2] as u16 >> 3)
}

fn bc1_encode_pixel(table: &[Rgba; 4], pixels: &[Rgba; 16]) -> u32 {
	let mut mask = 0u32;
	for pix in pixels.into_iter().rev() {
		let best = (0..4).into_iter().min_by_key(|x| bc1_dist(&table[*x], pix)).unwrap() as u32;
		mask = (mask << 2) | (best & 0x03);
	}
	mask
}

fn bc1_encode_block(pixels: &[Rgba; 16], quality: u8) -> (u64, u32) {
	/*let (min, max) = bc1_build_endpoints(&pixels)[0];
	let table = bc1_build_table(&min, &max);*/

	let ((min, max), table) = bc1_build_endpoints(pixels, quality).iter()
		.map(|pts| (*pts, bc1_build_table(&pts.0, &pts.1)))
		.min_by_key(|tbl| bc1_total_dist(&tbl.1, pixels)).unwrap();

	let dist = bc1_total_dist(&table, pixels);

	let min = bc1_encode_endpoint(&min) as u64;
	let max = bc1_encode_endpoint(&max) as u64;

	let encoded = bc1_encode_pixel(&table, &pixels) as u64;

	((encoded << 32) | (min << 16) | max, dist)
}

pub fn bc1(image: &Vec<u8>, size: (usize, usize), quality: u8) -> Result<Vec<u8>, ()> {
	if !(size.0 % 4 == 0 && size.1 % 4 == 0) {
		return Err(());
	}

    let mut out = Vec::with_capacity(image.len() / 8);
    let mut total_dist = 0u32;

    let block_size = 4;
    let blk_count = (size.0 / block_size, size.1 / block_size);

   	for x in 0..blk_count.0 {
   		for y in 0..blk_count.1 {

   			let mut pixels = [[0u8; 4]; 16]; // No compile time constants
   			for px in 0..block_size {
   				for py in 0..block_size {
   					let x = x * block_size + px;
   					let y = y * block_size + py;

   					let pi = (x * size.0 + y) * 4; // in bytes
   					let pix = [image[pi], image[pi + 1], image[pi + 2], image[pi + 3]];

   					pixels[px * block_size + py] = pix;
   				}
   			}

   			let (mut encoded, dist) = bc1_encode_block(&pixels, quality);
   			total_dist += dist;

   			for _ in 0..8 {
   				out.push((encoded & 0xFF) as u8);
   				encoded >>= 8;
   			}
		}
	}
	//println!("per-pixel dist = {:?}", total_dist / (size.0 * size.1) as u32);
	Ok(out)
}
<|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/. */

//! Configuration options for a single run of the servo application. Created
//! from command line arguments.

use geometry::ScreenPx;

use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use getopts;
use std::cmp;
use std::io;
use std::mem;
use std::os;
use std::ptr;
use std::rt;

/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
pub struct Opts {
    /// The initial URLs to load.
    pub urls: Vec<String>,

    /// How many threads to use for CPU rendering (`-t`).
    ///
    /// FIXME(pcwalton): This is not currently used. All rendering is sequential.
    pub n_render_threads: uint,

    /// True to use GPU painting via Skia-GL, false to use CPU painting via Skia (`-g`). Note that
    /// compositing is always done on the GPU.
    pub gpu_painting: bool,

    /// The maximum size of each tile in pixels (`-s`).
    pub tile_size: uint,

    /// The ratio of device pixels per px at the default scale. If unspecified, will use the
    /// platform default setting.
    pub device_pixels_per_px: Option<ScaleFactor<ScreenPx, DevicePixel, f32>>,

    /// `None` to disable the time profiler or `Some` with an interval in seconds to enable it and
    /// cause it to produce output on that interval (`-p`).
    pub time_profiler_period: Option<f64>,

    /// `None` to disable the memory profiler or `Some` with an interval in seconds to enable it
    /// and cause it to produce output on that interval (`-m`).
    pub memory_profiler_period: Option<f64>,

    /// Enable experimental web features (`-e`).
    pub enable_experimental: bool,

    /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive
    /// sequential algorithm.
    pub layout_threads: uint,

    pub nonincremental_layout: bool,

    /// True to exit after the page load (`-x`).
    pub exit_after_load: bool,

    pub output_file: Option<String>,
    pub headless: bool,
    pub hard_fail: bool,

    /// True if we should bubble intrinsic widths sequentially (`-b`). If this is true, then
    /// intrinsic widths are computed as a separate pass instead of during flow construction. You
    /// may wish to turn this flag on in order to benchmark style recalculation against other
    /// browser engines.
    pub bubble_inline_sizes_separately: bool,

    /// True if we should show borders on all layers and tiles for
    /// debugging purposes (`--show-debug-borders`).
    pub show_debug_borders: bool,

    /// True if we should show borders on all fragments for debugging purposes (`--show-debug-fragment-borders`).
    pub show_debug_fragment_borders: bool,

    /// If set with --disable-text-aa, disable antialiasing on fonts. This is primarily useful for reftests
    /// where pixel perfect results are required when using fonts such as the Ahem
    /// font for layout tests.
    pub enable_text_antialiasing: bool,

    /// True if each step of layout is traced to an external JSON file
    /// for debugging purposes. Settings this implies sequential layout
    /// and render.
    pub trace_layout: bool,

    /// `None` to disable devtools or `Some` with a port number to start a server to listen to
    /// remote Firefox devtools connections.
    pub devtools_port: Option<u16>,

    /// The initial requested size of the window.
    pub initial_window_size: TypedSize2D<ScreenPx, uint>,

    /// An optional string allowing the user agent to be set for testing.
    pub user_agent: Option<String>,

    /// Dumps the flow tree after a layout.
    pub dump_flow_tree: bool,

    /// Whether to show an error when display list geometry escapes flow overflow regions.
    pub validate_display_list_geometry: bool,
}

fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
    let message = format!("Usage: {} [ options ... ] [URL]\n\twhere options include", app);
    println!("{}", getopts::usage(message.as_slice(), opts));
}

fn args_fail(msg: &str) {
    io::stderr().write_line(msg).unwrap();
    os::set_exit_status(1);
}

// Always use CPU rendering on android.

#[cfg(target_os="android")]
static FORCE_CPU_PAINTING: bool = true;

#[cfg(not(target_os="android"))]
static FORCE_CPU_PAINTING: bool = false;

pub fn from_cmdline_args(args: &[String]) -> bool {
    let app_name = args[0].to_string();
    let args = args.tail();

    let opts = vec!(
        getopts::optflag("c", "cpu", "CPU painting (default)"),
        getopts::optflag("g", "gpu", "GPU painting"),
        getopts::optopt("o", "output", "Output file", "output.png"),
        getopts::optopt("r", "rendering", "Rendering backend", "direct2d|core-graphics|core-graphics-accelerated|cairo|skia."),
        getopts::optopt("s", "size", "Size of tiles", "512"),
        getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""),
        getopts::optflag("e", "experimental", "Enable experimental web features"),
        getopts::optopt("t", "threads", "Number of render threads", "1"),
        getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
        getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"),
        getopts::optflag("x", "exit", "Exit after load flag"),
        getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"),
        getopts::optflag("i", "nonincremental-layout", "Enable to turn off incremental layout."),
        getopts::optflag("z", "headless", "Headless mode"),
        getopts::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"),
        getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"),
        getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."),
        getopts::optflag("", "show-debug-fragment-borders", "Show debugging borders on fragments."),
        getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."),
        getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."),
        getopts::optflagopt("", "devtools", "Start remote devtools server on port", "6000"),
        getopts::optopt("", "resolution", "Set window resolution.", "800x600"),
        getopts::optopt("u", "user-agent", "Set custom user agent string", "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)"),
        getopts::optflag("", "dump-flow-tree", "Dump the flow (render) tree during each layout."),
        getopts::optflag("", "validate-display-list-geometry", "Display an error when display list geometry escapes overflow region."),
        getopts::optflag("h", "help", "Print this message")
    );

    let opt_match = match getopts::getopts(args, opts.as_slice()) {
        Ok(m) => m,
        Err(f) => {
            args_fail(format!("{}", f).as_slice());
            return false;
        }
    };

    if opt_match.opt_present("h") || opt_match.opt_present("help") {
        print_usage(app_name.as_slice(), opts.as_slice());
        return false;
    };

    let urls = if opt_match.free.is_empty() {
        print_usage(app_name.as_slice(), opts.as_slice());
        args_fail("servo asks that you provide 1 or more URLs");
        return false;
    } else {
        opt_match.free.clone()
    };

    let tile_size: uint = match opt_match.opt_str("s") {
        Some(tile_size_str) => from_str(tile_size_str.as_slice()).unwrap(),
        None => 512,
    };

    let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str|
        ScaleFactor(from_str(dppx_str.as_slice()).unwrap())
    );

    let mut n_render_threads: uint = match opt_match.opt_str("t") {
        Some(n_render_threads_str) => from_str(n_render_threads_str.as_slice()).unwrap(),
        None => 1,      // FIXME: Number of cores.
    };

    // If only the flag is present, default to a 5 second period for both profilers.
    let time_profiler_period = opt_match.opt_default("p", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });
    let memory_profiler_period = opt_match.opt_default("m", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });

    let gpu_painting = !FORCE_CPU_PAINTING && opt_match.opt_present("g");

    let mut layout_threads: uint = match opt_match.opt_str("y") {
        Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(),
        None => cmp::max(rt::default_sched_threads() * 3 / 4, 1),
    };

    let nonincremental_layout = opt_match.opt_present("i");

    let mut bubble_inline_sizes_separately = opt_match.opt_present("b");

    let trace_layout = opt_match.opt_present("trace-layout");
    if trace_layout {
        n_render_threads = 1;
        layout_threads = 1;
        bubble_inline_sizes_separately = true;
    }

    let devtools_port = opt_match.opt_default("devtools", "6000").map(|port| {
        from_str(port.as_slice()).unwrap()
    });

    let initial_window_size = match opt_match.opt_str("resolution") {
        Some(res_string) => {
            let res: Vec<uint> = res_string.as_slice().split('x').map(|r| from_str(r).unwrap()).collect();
            TypedSize2D(res[0], res[1])
        }
        None => {
            TypedSize2D(800, 600)
        }
    };

    let opts = Opts {
        urls: urls,
        n_render_threads: n_render_threads,
        gpu_painting: gpu_painting,
        tile_size: tile_size,
        device_pixels_per_px: device_pixels_per_px,
        time_profiler_period: time_profiler_period,
        memory_profiler_period: memory_profiler_period,
        enable_experimental: opt_match.opt_present("e"),
        layout_threads: layout_threads,
        nonincremental_layout: nonincremental_layout,
        exit_after_load: opt_match.opt_present("x"),
        output_file: opt_match.opt_str("o"),
        headless: opt_match.opt_present("z"),
        hard_fail: opt_match.opt_present("f"),
        bubble_inline_sizes_separately: bubble_inline_sizes_separately,
        show_debug_borders: opt_match.opt_present("show-debug-borders"),
        show_debug_fragment_borders: opt_match.opt_present("show-debug-fragment-borders"),
        enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"),
        trace_layout: trace_layout,
        devtools_port: devtools_port,
        initial_window_size: initial_window_size,
        user_agent: opt_match.opt_str("u"),
        dump_flow_tree: opt_match.opt_present("dump-flow-tree"),
        validate_display_list_geometry: opt_match.opt_present("validate-display-list-geometry"),
    };

    set_opts(opts);
    true
}

static mut EXPERIMENTAL_ENABLED: bool = false;

pub fn set_experimental_enabled(new_value: bool) {
    unsafe {
        EXPERIMENTAL_ENABLED = new_value;
    }
}

pub fn experimental_enabled() -> bool {
    unsafe {
        EXPERIMENTAL_ENABLED
    }
}

// Make Opts available globally. This saves having to clone and pass
// opts everywhere it is used, which gets particularly cumbersome
// when passing through the DOM structures.
static mut OPTIONS: *mut Opts = 0 as *mut Opts;

pub fn set_opts(opts: Opts) {
    unsafe {
        let box_opts = box opts;
        OPTIONS = mem::transmute(box_opts);
    }
}

#[inline]
pub fn get<'a>() -> &'a Opts {
    unsafe {
        assert!(OPTIONS != ptr::null_mut());
        mem::transmute(OPTIONS)
    }
}
<commit_msg>auto merge of #3742 : saneyuki/servo/opt, r=jdm<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/. */

//! Configuration options for a single run of the servo application. Created
//! from command line arguments.

use geometry::ScreenPx;

use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use getopts;
use std::cmp;
use std::io;
use std::mem;
use std::os;
use std::ptr;
use std::rt;

/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
pub struct Opts {
    /// The initial URLs to load.
    pub urls: Vec<String>,

    /// How many threads to use for CPU rendering (`-t`).
    ///
    /// FIXME(pcwalton): This is not currently used. All rendering is sequential.
    pub n_render_threads: uint,

    /// True to use GPU painting via Skia-GL, false to use CPU painting via Skia (`-g`). Note that
    /// compositing is always done on the GPU.
    pub gpu_painting: bool,

    /// The maximum size of each tile in pixels (`-s`).
    pub tile_size: uint,

    /// The ratio of device pixels per px at the default scale. If unspecified, will use the
    /// platform default setting.
    pub device_pixels_per_px: Option<ScaleFactor<ScreenPx, DevicePixel, f32>>,

    /// `None` to disable the time profiler or `Some` with an interval in seconds to enable it and
    /// cause it to produce output on that interval (`-p`).
    pub time_profiler_period: Option<f64>,

    /// `None` to disable the memory profiler or `Some` with an interval in seconds to enable it
    /// and cause it to produce output on that interval (`-m`).
    pub memory_profiler_period: Option<f64>,

    /// Enable experimental web features (`-e`).
    pub enable_experimental: bool,

    /// The number of threads to use for layout (`-y`). Defaults to 1, which results in a recursive
    /// sequential algorithm.
    pub layout_threads: uint,

    pub nonincremental_layout: bool,

    /// True to exit after the page load (`-x`).
    pub exit_after_load: bool,

    pub output_file: Option<String>,
    pub headless: bool,
    pub hard_fail: bool,

    /// True if we should bubble intrinsic widths sequentially (`-b`). If this is true, then
    /// intrinsic widths are computed as a separate pass instead of during flow construction. You
    /// may wish to turn this flag on in order to benchmark style recalculation against other
    /// browser engines.
    pub bubble_inline_sizes_separately: bool,

    /// True if we should show borders on all layers and tiles for
    /// debugging purposes (`--show-debug-borders`).
    pub show_debug_borders: bool,

    /// True if we should show borders on all fragments for debugging purposes (`--show-debug-fragment-borders`).
    pub show_debug_fragment_borders: bool,

    /// If set with --disable-text-aa, disable antialiasing on fonts. This is primarily useful for reftests
    /// where pixel perfect results are required when using fonts such as the Ahem
    /// font for layout tests.
    pub enable_text_antialiasing: bool,

    /// True if each step of layout is traced to an external JSON file
    /// for debugging purposes. Settings this implies sequential layout
    /// and render.
    pub trace_layout: bool,

    /// `None` to disable devtools or `Some` with a port number to start a server to listen to
    /// remote Firefox devtools connections.
    pub devtools_port: Option<u16>,

    /// The initial requested size of the window.
    pub initial_window_size: TypedSize2D<ScreenPx, uint>,

    /// An optional string allowing the user agent to be set for testing.
    pub user_agent: Option<String>,

    /// Dumps the flow tree after a layout.
    pub dump_flow_tree: bool,

    /// Whether to show an error when display list geometry escapes flow overflow regions.
    pub validate_display_list_geometry: bool,
}

fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
    let message = format!("Usage: {} [ options ... ] [URL]\n\twhere options include", app);
    println!("{}", getopts::usage(message.as_slice(), opts));
}

fn args_fail(msg: &str) {
    io::stderr().write_line(msg).unwrap();
    os::set_exit_status(1);
}

// Always use CPU rendering on android.

#[cfg(target_os="android")]
static FORCE_CPU_PAINTING: bool = true;

#[cfg(not(target_os="android"))]
static FORCE_CPU_PAINTING: bool = false;

pub fn from_cmdline_args(args: &[String]) -> bool {
    let app_name = args[0].to_string();
    let args = args.tail();

    let opts = vec!(
        getopts::optflag("c", "cpu", "CPU painting (default)"),
        getopts::optflag("g", "gpu", "GPU painting"),
        getopts::optopt("o", "output", "Output file", "output.png"),
        getopts::optopt("s", "size", "Size of tiles", "512"),
        getopts::optopt("", "device-pixel-ratio", "Device pixels per px", ""),
        getopts::optflag("e", "experimental", "Enable experimental web features"),
        getopts::optopt("t", "threads", "Number of render threads", "1"),
        getopts::optflagopt("p", "profile", "Profiler flag and output interval", "10"),
        getopts::optflagopt("m", "memory-profile", "Memory profiler flag and output interval", "10"),
        getopts::optflag("x", "exit", "Exit after load flag"),
        getopts::optopt("y", "layout-threads", "Number of threads to use for layout", "1"),
        getopts::optflag("i", "nonincremental-layout", "Enable to turn off incremental layout."),
        getopts::optflag("z", "headless", "Headless mode"),
        getopts::optflag("f", "hard-fail", "Exit on task failure instead of displaying about:failure"),
        getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"),
        getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."),
        getopts::optflag("", "show-debug-fragment-borders", "Show debugging borders on fragments."),
        getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."),
        getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."),
        getopts::optflagopt("", "devtools", "Start remote devtools server on port", "6000"),
        getopts::optopt("", "resolution", "Set window resolution.", "800x600"),
        getopts::optopt("u", "user-agent", "Set custom user agent string", "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)"),
        getopts::optflag("", "dump-flow-tree", "Dump the flow (render) tree during each layout."),
        getopts::optflag("", "validate-display-list-geometry", "Display an error when display list geometry escapes overflow region."),
        getopts::optflag("h", "help", "Print this message")
    );

    let opt_match = match getopts::getopts(args, opts.as_slice()) {
        Ok(m) => m,
        Err(f) => {
            args_fail(format!("{}", f).as_slice());
            return false;
        }
    };

    if opt_match.opt_present("h") || opt_match.opt_present("help") {
        print_usage(app_name.as_slice(), opts.as_slice());
        return false;
    };

    let urls = if opt_match.free.is_empty() {
        print_usage(app_name.as_slice(), opts.as_slice());
        args_fail("servo asks that you provide 1 or more URLs");
        return false;
    } else {
        opt_match.free.clone()
    };

    let tile_size: uint = match opt_match.opt_str("s") {
        Some(tile_size_str) => from_str(tile_size_str.as_slice()).unwrap(),
        None => 512,
    };

    let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str|
        ScaleFactor(from_str(dppx_str.as_slice()).unwrap())
    );

    let mut n_render_threads: uint = match opt_match.opt_str("t") {
        Some(n_render_threads_str) => from_str(n_render_threads_str.as_slice()).unwrap(),
        None => 1,      // FIXME: Number of cores.
    };

    // If only the flag is present, default to a 5 second period for both profilers.
    let time_profiler_period = opt_match.opt_default("p", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });
    let memory_profiler_period = opt_match.opt_default("m", "5").map(|period| {
        from_str(period.as_slice()).unwrap()
    });

    let gpu_painting = !FORCE_CPU_PAINTING && opt_match.opt_present("g");

    let mut layout_threads: uint = match opt_match.opt_str("y") {
        Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(),
        None => cmp::max(rt::default_sched_threads() * 3 / 4, 1),
    };

    let nonincremental_layout = opt_match.opt_present("i");

    let mut bubble_inline_sizes_separately = opt_match.opt_present("b");

    let trace_layout = opt_match.opt_present("trace-layout");
    if trace_layout {
        n_render_threads = 1;
        layout_threads = 1;
        bubble_inline_sizes_separately = true;
    }

    let devtools_port = opt_match.opt_default("devtools", "6000").map(|port| {
        from_str(port.as_slice()).unwrap()
    });

    let initial_window_size = match opt_match.opt_str("resolution") {
        Some(res_string) => {
            let res: Vec<uint> = res_string.as_slice().split('x').map(|r| from_str(r).unwrap()).collect();
            TypedSize2D(res[0], res[1])
        }
        None => {
            TypedSize2D(800, 600)
        }
    };

    let opts = Opts {
        urls: urls,
        n_render_threads: n_render_threads,
        gpu_painting: gpu_painting,
        tile_size: tile_size,
        device_pixels_per_px: device_pixels_per_px,
        time_profiler_period: time_profiler_period,
        memory_profiler_period: memory_profiler_period,
        enable_experimental: opt_match.opt_present("e"),
        layout_threads: layout_threads,
        nonincremental_layout: nonincremental_layout,
        exit_after_load: opt_match.opt_present("x"),
        output_file: opt_match.opt_str("o"),
        headless: opt_match.opt_present("z"),
        hard_fail: opt_match.opt_present("f"),
        bubble_inline_sizes_separately: bubble_inline_sizes_separately,
        show_debug_borders: opt_match.opt_present("show-debug-borders"),
        show_debug_fragment_borders: opt_match.opt_present("show-debug-fragment-borders"),
        enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"),
        trace_layout: trace_layout,
        devtools_port: devtools_port,
        initial_window_size: initial_window_size,
        user_agent: opt_match.opt_str("u"),
        dump_flow_tree: opt_match.opt_present("dump-flow-tree"),
        validate_display_list_geometry: opt_match.opt_present("validate-display-list-geometry"),
    };

    set_opts(opts);
    true
}

static mut EXPERIMENTAL_ENABLED: bool = false;

pub fn set_experimental_enabled(new_value: bool) {
    unsafe {
        EXPERIMENTAL_ENABLED = new_value;
    }
}

pub fn experimental_enabled() -> bool {
    unsafe {
        EXPERIMENTAL_ENABLED
    }
}

// Make Opts available globally. This saves having to clone and pass
// opts everywhere it is used, which gets particularly cumbersome
// when passing through the DOM structures.
static mut OPTIONS: *mut Opts = 0 as *mut Opts;

pub fn set_opts(opts: Opts) {
    unsafe {
        let box_opts = box opts;
        OPTIONS = mem::transmute(box_opts);
    }
}

#[inline]
pub fn get<'a>() -> &'a Opts {
    unsafe {
        assert!(OPTIONS != ptr::null_mut());
        mem::transmute(OPTIONS)
    }
}
<|endoftext|>