<commit_before>use inflector::Inflector;
use schemars::JsonSchema;
use serde::Serialize;
use std::path::Path;
use strum::{EnumIter, IntoEnumIterator};

#[derive(Debug, PartialEq, EnumIter)]
pub enum Format {
    Bash,
    Calc,
    Date,
    Directory,
    Dockerfile,
    Docx,
    Flac,
    Gif,
    Html,
    Ipynb,
    JavaScript,
    Jpeg,
    Json,
    Json5,
    LaTeX,
    Makefile,
    Markdown,
    Mp3,
    Mp4,
    Odt,
    Ogg,
    Ogv,
    Pandoc,
    Person,
    PlainText,
    Png,
    Python,
    R,
    RMarkdown,
    Rpng,
    Rust,
    Shell,
    ThreeGpp,
    Toml,
    TypeScript,
    Unknown,
    WebM,
    Xml,
    Yaml,
    Zsh,
}

impl Format {
    /// A list of known formats
    #[rustfmt::skip]
    pub fn spec(&self) -> FormatSpec {
        match self {

            // Data serialization formats. These may be used to store documents so `preview: true`.
            Format::Json => FormatSpec::new("JSON", "json", &[], false, true, FormatNodeType::Unknown),
            Format::Json5 => FormatSpec::new("JSON5", "json5", &[], false, true, FormatNodeType::Unknown),

            Format::Toml => FormatSpec::new("TOML", "toml", &[], false, true, FormatNodeType::Unknown),
            Format::Xml => FormatSpec::new("XML", "xml", &[], false, true, FormatNodeType::Unknown),
            Format::Yaml => FormatSpec::new("YAML", "yaml", &[], false, true, FormatNodeType::Unknown),

            // Code formats
            Format::Bash => FormatSpec::new("Bash", "bash", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Calc => FormatSpec::new("Calc", "calc", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Dockerfile => FormatSpec::new("Dockerfile", "dockerfile", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::JavaScript => FormatSpec::new("JavaScript", "js", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Makefile => FormatSpec::new("Makefile", "makefile", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Python => FormatSpec::new("Python", "py", &["python3"], false, false, FormatNodeType::SoftwareSourceCode),
            Format::R => FormatSpec::new("R", "r", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Rust => FormatSpec::new("Rust", "rust", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Shell => FormatSpec::new("Shell", "sh", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::TypeScript => FormatSpec::new("TypeScript", "ts", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Zsh => FormatSpec::new("ZSH", "zsh", &[], false, false, FormatNodeType::SoftwareSourceCode),

            // Article formats
            Format::Docx => FormatSpec::new("Microsoft Word", "docx", &[], true, true, FormatNodeType::Article),
            Format::Html => FormatSpec::new("HTML", "html", &[], false, true, FormatNodeType::Article),
            Format::Ipynb => FormatSpec::new("Jupyter Notebook", "ipynb", &[], false, true, FormatNodeType::Article),
            Format::Markdown => FormatSpec::new("Markdown", "md", &[], false, true, FormatNodeType::Article),
            Format::Odt => FormatSpec::new("Open Office Text", "odt", &[], true, true, FormatNodeType::Article),
            Format::Pandoc => FormatSpec::new("Pandoc JSON", "pandoc", &[], false, true, FormatNodeType::Article),
            Format::RMarkdown => FormatSpec::new("R Markdown", "rmd", &[], false, true, FormatNodeType::Article),
            Format::LaTeX => FormatSpec::new("LaTeX", "latex", &["tex"], false, true, FormatNodeType::Article),

            // Audio formats
            Format::Flac => FormatSpec::new("FLAC", "flac", &[], true, true, FormatNodeType::AudioObject),
            Format::Mp3 => FormatSpec::new("MP3", "mp3", &[], true, true, FormatNodeType::AudioObject),
            Format::Ogg => FormatSpec::new("Ogg", "ogg", &[], true, true, FormatNodeType::AudioObject),

            // Image formats
            Format::Gif => FormatSpec::new("GIF", "gif", &[], true, true, FormatNodeType::ImageObject),
            Format::Jpeg => FormatSpec::new("JPEG", "jpg", &["jpeg"], true, true, FormatNodeType::ImageObject),
            Format::Png => FormatSpec::new("PNG", "png", &[], true, true, FormatNodeType::ImageObject),
            Format::Rpng => FormatSpec::new("RPNG", "rpng", &[], true, true, FormatNodeType::ImageObject),

            // Video formats
            Format::ThreeGpp => FormatSpec::new("3GPP", "3gp", &[], true, true, FormatNodeType::VideoObject),
            Format::Mp4 => FormatSpec::new("MP4", "mp4", &[], true, true, FormatNodeType::VideoObject),
            Format::Ogv => FormatSpec::new("Ogg Video", "ogv", &[], true, true, FormatNodeType::VideoObject),
            Format::WebM => FormatSpec::new("WebM", "webm", &[], true, true, FormatNodeType::VideoObject),

            // Other
            Format::PlainText => FormatSpec::new("Plain text", "txt", &[], false, false, FormatNodeType::Unknown),
            Format::Person => FormatSpec::new("Person", "person", &[], false, false, FormatNodeType::Person),
            Format::Date => FormatSpec::new("Date", "date", &[], false, false, FormatNodeType::Date),

            // Specials
            Format::Directory => FormatSpec::directory(),
            Format::Unknown => FormatSpec::unknown("unknown")
        }
    }
}

/// The type of format as a schema `Node` type
#[derive(Clone, Debug, PartialEq, JsonSchema, Serialize)]
pub enum FormatNodeType {
    Article,
    AudioObject,
    ImageObject,
    VideoObject,
    SoftwareSourceCode,
    Date,
    Person,
    Unknown,
}

/// Specification of a format
///
/// Used to determine various application behaviors
/// e.g. not reading binary formats into memory unnecessarily
#[derive(Clone, Debug, PartialEq, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct FormatSpec {
    /// The title of the format e.g. "Markdown"
    pub title: String,

    /// The extension to use e.g. "md" when saving a file in this format
    ///
    /// Note: this is the "default" extension but other extensions can be
    /// listed in `aliases`.
    pub extension: String,

    /// Any additional names or extensions that this format
    /// should match against (should be lowercase).
    pub aliases: Vec<String>,

    /// Whether or not the format should be considered binary
    /// e.g. not to be displayed in a text / code editor
    pub binary: bool,

    /// Whether HTML previews are normally supported for documents of
    /// this format. See also `Document.previewable` which indicates whether
    /// a HTML preview is supported for a particular document.
    pub preview: bool,

    /// The kind of format
    pub node_type: FormatNodeType,

    /// Whether or not this is a known format specification (i.e. not automatically created)
    pub known: bool,
}

impl FormatSpec {
    /// Create a new format spec
    pub fn new(
        title: &str,
        extension: &str,
        aliases: &[&str],
        binary: bool,
        preview: bool,
        node_type: FormatNodeType,
    ) -> FormatSpec {
        FormatSpec {
            title: title.into(),
            extension: extension.into(),
            aliases: aliases.iter().map(|s| s.to_string()).collect(),
            binary,
            preview,
            node_type,
            known: true,
        }
    }

    /// Create the special `directory` format used on `File` objects
    /// that are directories
    pub fn directory() -> FormatSpec {
        FormatSpec {
            title: "Directory".to_string(),
            extension: "".into(),
            aliases: Vec::new(),
            binary: true,
            preview: false,
            node_type: FormatNodeType::Unknown,
            known: true,
        }
    }

    /// Create the special `unknown` file format where all we
    /// have is the name e.g. from a file extension.
    pub fn unknown(name: &str) -> FormatSpec {
        FormatSpec {
            title: name.to_string(),
            extension: name.to_lowercase(),
            aliases: Vec::new(),
            // Set binary to false so that any unregistered format
            // will be at least shown in editor...
            binary: false,
            // ..but not have a preview
            preview: false,
            node_type: FormatNodeType::Unknown,
            known: false,
        }
    }
}

/// Match a format name to a `Format`
///
/// Iterates over the `Format` variants and returns the first
/// that has a title, extension or aliases that match it.
pub fn match_name(name: &str) -> Format {
    let name = name.trim();

    // Early return for empty strings to avoid matching `Directory`
    // which has an empty extension.
    if name.is_empty() {
        return Format::Unknown;
    }

    let name = name.to_lowercase();
    for format in Format::iter() {
        let spec = format.spec();
        if name == spec.title.to_lowercase()
            || name == spec.extension
            || spec.aliases.contains(&name)
        {
            return format;
        }
    }

    Format::Unknown
}

/// Match a file path to a `Format`
///
/// Extracts a "name" (extension or file base name) from a
/// file path and then calls `match_name` on that.
pub fn match_path<P: AsRef<Path>>(path: &P) -> Format {
    let path = path.as_ref();

    // Get name from file extension, or filename if no extension
    let name = match path.extension() {
        Some(ext) => ext,
        None => match path.file_name() {
            Some(name) => name,
            // Fallback to the provided "path"
            None => path.as_os_str(),
        },
    };

    // Match that name
    match_name(&name.to_string_lossy().to_string())
}

/// Normalize a format name to its title
///
/// If the name can be matched against a known format then its
/// title will be returned. Otherwise, the name converted to title
/// case will be returned.
pub fn normalize_title(name: &str) -> String {
    let format = match_name(name);
    match format {
        Format::Unknown => name.to_title_case(),
        _ => format.spec().title,
    }
}
<commit_msg>chore(Formats): Add Jullia as a recognised format<commit_after>use inflector::Inflector;
use schemars::JsonSchema;
use serde::Serialize;
use std::path::Path;
use strum::{EnumIter, IntoEnumIterator};

#[derive(Debug, PartialEq, EnumIter)]
pub enum Format {
    Bash,
    Calc,
    Date,
    Directory,
    Dockerfile,
    Docx,
    Flac,
    Gif,
    Html,
    Ipynb,
    JavaScript,
    Jpeg,
    Json,
    Json5,
    Julia,
    LaTeX,
    Makefile,
    Markdown,
    Mp3,
    Mp4,
    Odt,
    Ogg,
    Ogv,
    Pandoc,
    Person,
    PlainText,
    Png,
    Python,
    R,
    RMarkdown,
    Rpng,
    Rust,
    Shell,
    ThreeGpp,
    Toml,
    TypeScript,
    Unknown,
    WebM,
    Xml,
    Yaml,
    Zsh,
}

impl Format {
    /// A list of known formats
    #[rustfmt::skip]
    pub fn spec(&self) -> FormatSpec {
        match self {

            // Data serialization formats. These may be used to store documents so `preview: true`.
            Format::Json => FormatSpec::new("JSON", "json", &[], false, true, FormatNodeType::Unknown),
            Format::Json5 => FormatSpec::new("JSON5", "json5", &[], false, true, FormatNodeType::Unknown),

            Format::Toml => FormatSpec::new("TOML", "toml", &[], false, true, FormatNodeType::Unknown),
            Format::Xml => FormatSpec::new("XML", "xml", &[], false, true, FormatNodeType::Unknown),
            Format::Yaml => FormatSpec::new("YAML", "yaml", &[], false, true, FormatNodeType::Unknown),

            // Code formats
            Format::Bash => FormatSpec::new("Bash", "bash", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Calc => FormatSpec::new("Calc", "calc", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Dockerfile => FormatSpec::new("Dockerfile", "dockerfile", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::JavaScript => FormatSpec::new("JavaScript", "js", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Julia => FormatSpec::new("Julia", "jl", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Makefile => FormatSpec::new("Makefile", "makefile", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Python => FormatSpec::new("Python", "py", &["python3"], false, false, FormatNodeType::SoftwareSourceCode),
            Format::R => FormatSpec::new("R", "r", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Rust => FormatSpec::new("Rust", "rust", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Shell => FormatSpec::new("Shell", "sh", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::TypeScript => FormatSpec::new("TypeScript", "ts", &[], false, false, FormatNodeType::SoftwareSourceCode),
            Format::Zsh => FormatSpec::new("ZSH", "zsh", &[], false, false, FormatNodeType::SoftwareSourceCode),

            // Article formats
            Format::Docx => FormatSpec::new("Microsoft Word", "docx", &[], true, true, FormatNodeType::Article),
            Format::Html => FormatSpec::new("HTML", "html", &[], false, true, FormatNodeType::Article),
            Format::Ipynb => FormatSpec::new("Jupyter Notebook", "ipynb", &[], false, true, FormatNodeType::Article),
            Format::Markdown => FormatSpec::new("Markdown", "md", &[], false, true, FormatNodeType::Article),
            Format::Odt => FormatSpec::new("Open Office Text", "odt", &[], true, true, FormatNodeType::Article),
            Format::Pandoc => FormatSpec::new("Pandoc JSON", "pandoc", &[], false, true, FormatNodeType::Article),
            Format::RMarkdown => FormatSpec::new("R Markdown", "rmd", &[], false, true, FormatNodeType::Article),
            Format::LaTeX => FormatSpec::new("LaTeX", "latex", &["tex"], false, true, FormatNodeType::Article),

            // Audio formats
            Format::Flac => FormatSpec::new("FLAC", "flac", &[], true, true, FormatNodeType::AudioObject),
            Format::Mp3 => FormatSpec::new("MP3", "mp3", &[], true, true, FormatNodeType::AudioObject),
            Format::Ogg => FormatSpec::new("Ogg", "ogg", &[], true, true, FormatNodeType::AudioObject),

            // Image formats
            Format::Gif => FormatSpec::new("GIF", "gif", &[], true, true, FormatNodeType::ImageObject),
            Format::Jpeg => FormatSpec::new("JPEG", "jpg", &["jpeg"], true, true, FormatNodeType::ImageObject),
            Format::Png => FormatSpec::new("PNG", "png", &[], true, true, FormatNodeType::ImageObject),
            Format::Rpng => FormatSpec::new("RPNG", "rpng", &[], true, true, FormatNodeType::ImageObject),

            // Video formats
            Format::ThreeGpp => FormatSpec::new("3GPP", "3gp", &[], true, true, FormatNodeType::VideoObject),
            Format::Mp4 => FormatSpec::new("MP4", "mp4", &[], true, true, FormatNodeType::VideoObject),
            Format::Ogv => FormatSpec::new("Ogg Video", "ogv", &[], true, true, FormatNodeType::VideoObject),
            Format::WebM => FormatSpec::new("WebM", "webm", &[], true, true, FormatNodeType::VideoObject),

            // Other
            Format::PlainText => FormatSpec::new("Plain text", "txt", &[], false, false, FormatNodeType::Unknown),
            Format::Person => FormatSpec::new("Person", "person", &[], false, false, FormatNodeType::Person),
            Format::Date => FormatSpec::new("Date", "date", &[], false, false, FormatNodeType::Date),

            // Specials
            Format::Directory => FormatSpec::directory(),
            Format::Unknown => FormatSpec::unknown("unknown")
        }
    }
}

/// The type of format as a schema `Node` type
#[derive(Clone, Debug, PartialEq, JsonSchema, Serialize)]
pub enum FormatNodeType {
    Article,
    AudioObject,
    ImageObject,
    VideoObject,
    SoftwareSourceCode,
    Date,
    Person,
    Unknown,
}

/// Specification of a format
///
/// Used to determine various application behaviors
/// e.g. not reading binary formats into memory unnecessarily
#[derive(Clone, Debug, PartialEq, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct FormatSpec {
    /// The title of the format e.g. "Markdown"
    pub title: String,

    /// The extension to use e.g. "md" when saving a file in this format
    ///
    /// Note: this is the "default" extension but other extensions can be
    /// listed in `aliases`.
    pub extension: String,

    /// Any additional names or extensions that this format
    /// should match against (should be lowercase).
    pub aliases: Vec<String>,

    /// Whether or not the format should be considered binary
    /// e.g. not to be displayed in a text / code editor
    pub binary: bool,

    /// Whether HTML previews are normally supported for documents of
    /// this format. See also `Document.previewable` which indicates whether
    /// a HTML preview is supported for a particular document.
    pub preview: bool,

    /// The kind of format
    pub node_type: FormatNodeType,

    /// Whether or not this is a known format specification (i.e. not automatically created)
    pub known: bool,
}

impl FormatSpec {
    /// Create a new format spec
    pub fn new(
        title: &str,
        extension: &str,
        aliases: &[&str],
        binary: bool,
        preview: bool,
        node_type: FormatNodeType,
    ) -> FormatSpec {
        FormatSpec {
            title: title.into(),
            extension: extension.into(),
            aliases: aliases.iter().map(|s| s.to_string()).collect(),
            binary,
            preview,
            node_type,
            known: true,
        }
    }

    /// Create the special `directory` format used on `File` objects
    /// that are directories
    pub fn directory() -> FormatSpec {
        FormatSpec {
            title: "Directory".to_string(),
            extension: "".into(),
            aliases: Vec::new(),
            binary: true,
            preview: false,
            node_type: FormatNodeType::Unknown,
            known: true,
        }
    }

    /// Create the special `unknown` file format where all we
    /// have is the name e.g. from a file extension.
    pub fn unknown(name: &str) -> FormatSpec {
        FormatSpec {
            title: name.to_string(),
            extension: name.to_lowercase(),
            aliases: Vec::new(),
            // Set binary to false so that any unregistered format
            // will be at least shown in editor...
            binary: false,
            // ..but not have a preview
            preview: false,
            node_type: FormatNodeType::Unknown,
            known: false,
        }
    }
}

/// Match a format name to a `Format`
///
/// Iterates over the `Format` variants and returns the first
/// that has a title, extension or aliases that match it.
pub fn match_name(name: &str) -> Format {
    let name = name.trim();

    // Early return for empty strings to avoid matching `Directory`
    // which has an empty extension.
    if name.is_empty() {
        return Format::Unknown;
    }

    let name = name.to_lowercase();
    for format in Format::iter() {
        let spec = format.spec();
        if name == spec.title.to_lowercase()
            || name == spec.extension
            || spec.aliases.contains(&name)
        {
            return format;
        }
    }

    Format::Unknown
}

/// Match a file path to a `Format`
///
/// Extracts a "name" (extension or file base name) from a
/// file path and then calls `match_name` on that.
pub fn match_path<P: AsRef<Path>>(path: &P) -> Format {
    let path = path.as_ref();

    // Get name from file extension, or filename if no extension
    let name = match path.extension() {
        Some(ext) => ext,
        None => match path.file_name() {
            Some(name) => name,
            // Fallback to the provided "path"
            None => path.as_os_str(),
        },
    };

    // Match that name
    match_name(&name.to_string_lossy().to_string())
}

/// Normalize a format name to its title
///
/// If the name can be matched against a known format then its
/// title will be returned. Otherwise, the name converted to title
/// case will be returned.
pub fn normalize_title(name: &str) -> String {
    let format = match_name(name);
    match format {
        Format::Unknown => name.to_title_case(),
        _ => format.spec().title,
    }
}
<|endoftext|>


<commit_before>use syntax::ast::{
    Ident,
    MetaItem,
    Item,
    Expr,
    StructDef,
};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use aster;

use field::field_alias;

pub fn expand_derive_serialize(
    cx: &mut ExtCtxt,
    span: Span,
    _mitem: &MetaItem,
    item: &Item,
    push: &mut FnMut(P<ast::Item>)
) {
    let builder = aster::AstBuilder::new().span(span);

    let generics = match item.node {
        ast::ItemStruct(_, ref generics) => generics,
        ast::ItemEnum(_, ref generics) => generics,
        _ => cx.bug("expected ItemStruct or ItemEnum in derive(Deserialize)")
    };

    let impl_generics = builder.from_generics(generics.clone())
        .add_ty_param_bound(
            builder.path().global().ids(&["serde", "ser", "Serialize"]).build()
        )
        .build();

    let serialize_ty = builder.ty().path()
        .segment(item.ident).with_generics(impl_generics.clone()).build()
        .build();

    let body = serialize_body(
        cx,
        &builder,
        item,
        &impl_generics,
        serialize_ty.clone(),
    );

    let where_clause = &impl_generics.where_clause;

    let impl_item = quote_item!(cx,
        #[automatically_derived]
        impl $impl_generics ::serde::ser::Serialize for $serialize_ty $where_clause {
            fn serialize<__S>(&self, serializer: &mut __S) -> Result<(), __S::Error>
                where __S: serde::ser::Serializer,
            {
                $body
            }
        }
    ).unwrap();

    push(impl_item)
}

fn serialize_body(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    item: &Item,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
) -> P<ast::Expr> {
    match item.node {
        ast::ItemStruct(ref struct_def, _) => {
            serialize_item_struct(
                cx,
                builder,
                item,
                impl_generics,
                serialize_ty,
                struct_def,
            )
        }

        ast::ItemEnum(ref enum_def, _) => {
            serialize_item_enum(
                cx,
                builder,
                item.ident,
                impl_generics,
                enum_def,
            )
        }

        _ => cx.bug("expected ItemStruct or ItemEnum in derive_serialize")
    }
}

fn serialize_item_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    item: &Item,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
    struct_def: &ast::StructDef,
) -> P<ast::Expr> {
    let mut named_fields = vec![];
    let mut unnamed_fields = 0;

    for field in struct_def.fields.iter() {
        match field.node.kind {
            ast::NamedField(name, _) => { named_fields.push(name); }
            ast::UnnamedField(_) => { unnamed_fields += 1; }
        }
    }

    match (named_fields.is_empty(), unnamed_fields == 0) {
        (true, true) => {
            serialize_unit_struct(
                cx,
                &builder,
                item.ident,
            )
        }
        (true, false) => {
            serialize_tuple_struct(
                cx,
                &builder,
                item.ident,
                impl_generics,
                serialize_ty,
                unnamed_fields,
            )
        }
        (false, true) => {
            serialize_struct(
                cx,
                &builder,
                item.ident,
                impl_generics,
                serialize_ty,
                struct_def,
                named_fields,
            )
        }
        (false, false) => {
            cx.bug("struct has named and unnamed fields")
        }
    }
}

fn serialize_unit_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident
) -> P<ast::Expr> {
    let type_name = builder.expr().str(type_ident);

    quote_expr!(cx, serializer.visit_named_unit($type_name))
}

fn serialize_tuple_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
    fields: usize,
) -> P<ast::Expr> {
    let value_ty = builder.ty()
        .ref_()
            .lifetime("'__a")
            .build_ty(serialize_ty);

    let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
        cx,
        builder,
        value_ty,
        fields,
        impl_generics,
    );

    let type_name = builder.expr().str(type_ident);

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_named_seq($type_name, Visitor {
            value: self,
            state: 0,
        })
    })
}

fn serialize_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
    struct_def: &StructDef,
    fields: Vec<Ident>,
) -> P<ast::Expr> {
    let value_ty = builder.ty()
        .ref_()
            .lifetime("'__a")
            .build_ty(serialize_ty.clone());

    let (visitor_struct, visitor_impl) = serialize_struct_visitor(
        cx,
        builder,
        value_ty,
        struct_def,
        impl_generics,
        fields.iter().map(|field| quote_expr!(cx, &self.value.$field)),
    );

    let type_name = builder.expr().str(type_ident);

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_named_map($type_name, Visitor {
            value: self,
            state: 0,
        })
    })
}

fn serialize_item_enum(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    impl_generics: &ast::Generics,
    enum_def: &ast::EnumDef,
) -> P<ast::Expr> {
    let arms: Vec<ast::Arm> = enum_def.variants.iter()
        .map(|variant| {
            serialize_variant(
                cx,
                builder,
                type_ident,
                impl_generics,
                variant,
            )
        })
        .collect();

    quote_expr!(cx,
        match *self {
            $arms
        }
    )
}

fn serialize_variant(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    generics: &ast::Generics,
    variant: &ast::Variant,
) -> ast::Arm {
    let type_name = builder.expr().str(type_ident);
    let variant_ident = variant.node.name;
    let variant_name = builder.expr().str(variant_ident);

    match variant.node.kind {
        ast::TupleVariantKind(ref args) if args.is_empty() => {
            let pat = builder.pat().enum_()
                .id(type_ident).id(variant_ident).build()
                .build();

            quote_arm!(cx,
                $pat => {
                    ::serde::ser::Serializer::visit_enum_unit(
                        serializer,
                        $type_name,
                        $variant_name,
                    )
                },
            )
        }
        ast::TupleVariantKind(ref args) => {
            let fields: Vec<ast::Ident> = (0 .. args.len())
                .map(|i| builder.id(format!("__field{}", i)))
                .collect();

            let pat = builder.pat().enum_()
                .id(type_ident).id(variant_ident).build()
                .with_pats(fields.iter().map(|field| builder.pat().ref_id(field)))
                .build();

            let expr = serialize_tuple_variant(
                cx,
                builder,
                type_name,
                variant_name,
                generics,
                args,
                fields,
            );

            quote_arm!(cx, $pat => { $expr })
        }
        ast::StructVariantKind(ref struct_def) => {
            let fields: Vec<_> = (0 .. struct_def.fields.len())
                .map(|i| builder.id(format!("__field{}", i)))
                .collect();

            let pat = builder.pat().struct_()
                .id(type_ident).id(variant_ident).build()
                .with_pats(
                    fields.iter()
                        .zip(struct_def.fields.iter())
                        .map(|(id, field)| {
                            let name = match field.node.kind {
                                ast::NamedField(name, _) => name,
                                ast::UnnamedField(_) => {
                                    cx.bug("struct variant has unnamed fields")
                                }
                            };

                            (name, builder.pat().ref_id(id))
                        })
                )
                .build();

            let expr = serialize_struct_variant(
                cx,
                builder,
                type_name,
                variant_name,
                generics,
                struct_def,
                fields,
            );

            quote_arm!(cx, $pat => { $expr })
        }
    }
}

fn serialize_tuple_variant(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_name: P<ast::Expr>,
    variant_name: P<ast::Expr>,
    generics: &ast::Generics,
    args: &[ast::VariantArg],
    fields: Vec<Ident>,
) -> P<ast::Expr> {
    let value_ty = builder.ty().tuple()
        .with_tys(
            args.iter().map(|arg| {
                builder.ty()
                    .ref_()
                    .lifetime("'__a")
                    .build_ty(arg.ty.clone())
            })
        )
        .build();

    let value_expr = builder.expr().tuple()
        .with_exprs(
            fields.iter().map(|field| {
                builder.expr()
                    .addr_of()
                    .id(field)
            })
        )
        .build();

    let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
        cx,
        builder,
        value_ty,
        args.len(),
        generics,
    );

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_enum_seq($type_name, $variant_name, Visitor {
            value: $value_expr,
            state: 0,
        })
    })
}

fn serialize_struct_variant(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_name: P<ast::Expr>,
    variant_name: P<ast::Expr>,
    generics: &ast::Generics,
    struct_def: &ast::StructDef,
    fields: Vec<Ident>,
) -> P<ast::Expr> {
    let value_ty = builder.ty().tuple()
        .with_tys(
            struct_def.fields.iter().map(|field| {
                builder.ty()
                    .ref_()
                    .lifetime("'__a")
                    .build_ty(field.node.ty.clone())
            })
        )
        .build();

    let value_expr = builder.expr().tuple()
        .with_exprs(
            fields.iter().map(|field| {
                builder.expr()
                    .addr_of()
                    .id(field)
            })
        )
        .build();

    let (visitor_struct, visitor_impl) = serialize_struct_visitor(
        cx,
        builder,
        value_ty,
        struct_def,
        generics,
        (0 .. fields.len()).map(|i| {
            builder.expr()
                .tup_field(i)
                .field("value").self_()
        })
    );

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_enum_map($type_name, $variant_name, Visitor {
            value: $value_expr,
            state: 0,
        })
    })
}

fn serialize_tuple_struct_visitor(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    value_ty: P<ast::Ty>,
    fields: usize,
    generics: &ast::Generics
) -> (P<ast::Item>, P<ast::Item>) {
    let arms: Vec<ast::Arm> = (0 .. fields)
        .map(|i| {
            let first = builder.expr().bool(i == 0);
            let expr = builder.expr()
                .tup_field(i)
                .field("value").self_();

            quote_arm!(cx,
                $i => {
                    self.state += 1;
                    let v = try!(serializer.visit_seq_elt($first, &$expr));
                    Ok(Some(v))
                }
            )
        })
        .collect();

    let visitor_impl_generics = builder.from_generics(generics.clone())
        .add_lifetime_bound("'__a")
        .add_ty_param_bound(
            builder.path().global().ids(&["serde", "ser", "Serialize"]).build()
        )
        .lifetime_name("'__a")
        .build();

    let where_clause = &visitor_impl_generics.where_clause;

    let visitor_generics = builder.from_generics(visitor_impl_generics.clone())
        .strip_bounds()
        .build();

    (
        quote_item!(cx,
            struct Visitor $visitor_impl_generics $where_clause {
                state: usize,
                value: $value_ty,
            }
        ).unwrap(),

        quote_item!(cx,
            impl $visitor_impl_generics ::serde::ser::SeqVisitor
            for Visitor $visitor_generics
            $where_clause {
                #[inline]
                fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
                    where S: ::serde::ser::Serializer,
                {
                    match self.state {
                        $arms
                        _ => Ok(None),
                    }
                }

                #[inline]
                fn len(&self) -> Option<usize> {
                    Some($fields)
                }
            }
        ).unwrap(),
    )
}

fn serialize_struct_visitor<I>(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    value_ty: P<ast::Ty>,
    struct_def: &StructDef,
    generics: &ast::Generics,
    value_exprs: I,
) -> (P<ast::Item>, P<ast::Item>)
    where I: Iterator<Item=P<ast::Expr>>,
{
    let len = struct_def.fields.len();

    let key_exprs = struct_def.fields.iter()
        .map(|field| {
            match field_alias(field) {
                Some(lit) => builder.expr().build_lit(P(lit.clone())),
                None => {
                    match field.node.kind {
                        ast::NamedField(name, _) => {
                            builder.expr().str(name)
                        }
                        ast::UnnamedField(_) => {
                            cx.bug("struct has named and unnamed fields")
                        }
                    }
                }
            }
        });

    let arms: Vec<ast::Arm> = key_exprs
        .zip(value_exprs)
        .enumerate()
        .map(|(i, (key_expr, value_expr))| {
            let first = i == 0;

            quote_arm!(cx,
                $i => {
                    self.state += 1;
                    Ok(
                        Some(
                            try!(
                                serializer.visit_map_elt(
                                    $first,
                                    $key_expr,
                                    $value_expr,
                                )
                            )
                        )
                    )
                }
            )
        })
        .collect();

    let visitor_impl_generics = builder.from_generics(generics.clone())
        .add_lifetime_bound("'__a")
        .add_ty_param_bound(
            builder.path().global().ids(&["serde", "ser", "Serialize"]).build()
        )
        .lifetime_name("'__a")
        .build();

    let where_clause = &visitor_impl_generics.where_clause;

    let visitor_generics = builder.from_generics(visitor_impl_generics.clone())
        .strip_bounds()
        .build();

    (
        quote_item!(cx,
            struct Visitor $visitor_impl_generics $where_clause {
                state: usize,
                value: $value_ty,
            }
        ).unwrap(),

        quote_item!(cx,
            impl $visitor_impl_generics
            ::serde::ser::MapVisitor
            for Visitor $visitor_generics
            $where_clause {
                #[inline]
                fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
                    where S: ::serde::ser::Serializer,
                {
                    match self.state {
                        $arms
                        _ => Ok(None),
                    }
                }

                #[inline]
                fn len(&self) -> Option<usize> {
                    Some($len)
                }
            }
        ).unwrap(),
    )
}
<commit_msg>Remove redundant bounds<commit_after>use syntax::ast::{
    Ident,
    MetaItem,
    Item,
    Expr,
    StructDef,
};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;

use aster;

use field::field_alias;

pub fn expand_derive_serialize(
    cx: &mut ExtCtxt,
    span: Span,
    _mitem: &MetaItem,
    item: &Item,
    push: &mut FnMut(P<ast::Item>)
) {
    let builder = aster::AstBuilder::new().span(span);

    let generics = match item.node {
        ast::ItemStruct(_, ref generics) => generics,
        ast::ItemEnum(_, ref generics) => generics,
        _ => cx.bug("expected ItemStruct or ItemEnum in derive(Deserialize)")
    };

    let impl_generics = builder.from_generics(generics.clone())
        .add_ty_param_bound(
            builder.path().global().ids(&["serde", "ser", "Serialize"]).build()
        )
        .build();

    let serialize_ty = builder.ty().path()
        .segment(item.ident).with_generics(impl_generics.clone()).build()
        .build();

    let body = serialize_body(
        cx,
        &builder,
        item,
        &impl_generics,
        serialize_ty.clone(),
    );

    let where_clause = &impl_generics.where_clause;

    let impl_item = quote_item!(cx,
        #[automatically_derived]
        impl $impl_generics ::serde::ser::Serialize for $serialize_ty $where_clause {
            fn serialize<__S>(&self, serializer: &mut __S) -> Result<(), __S::Error>
                where __S: serde::ser::Serializer,
            {
                $body
            }
        }
    ).unwrap();

    push(impl_item)
}

fn serialize_body(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    item: &Item,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
) -> P<ast::Expr> {
    match item.node {
        ast::ItemStruct(ref struct_def, _) => {
            serialize_item_struct(
                cx,
                builder,
                item,
                impl_generics,
                serialize_ty,
                struct_def,
            )
        }

        ast::ItemEnum(ref enum_def, _) => {
            serialize_item_enum(
                cx,
                builder,
                item.ident,
                impl_generics,
                enum_def,
            )
        }

        _ => cx.bug("expected ItemStruct or ItemEnum in derive_serialize")
    }
}

fn serialize_item_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    item: &Item,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
    struct_def: &ast::StructDef,
) -> P<ast::Expr> {
    let mut named_fields = vec![];
    let mut unnamed_fields = 0;

    for field in struct_def.fields.iter() {
        match field.node.kind {
            ast::NamedField(name, _) => { named_fields.push(name); }
            ast::UnnamedField(_) => { unnamed_fields += 1; }
        }
    }

    match (named_fields.is_empty(), unnamed_fields == 0) {
        (true, true) => {
            serialize_unit_struct(
                cx,
                &builder,
                item.ident,
            )
        }
        (true, false) => {
            serialize_tuple_struct(
                cx,
                &builder,
                item.ident,
                impl_generics,
                serialize_ty,
                unnamed_fields,
            )
        }
        (false, true) => {
            serialize_struct(
                cx,
                &builder,
                item.ident,
                impl_generics,
                serialize_ty,
                struct_def,
                named_fields,
            )
        }
        (false, false) => {
            cx.bug("struct has named and unnamed fields")
        }
    }
}

fn serialize_unit_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident
) -> P<ast::Expr> {
    let type_name = builder.expr().str(type_ident);

    quote_expr!(cx, serializer.visit_named_unit($type_name))
}

fn serialize_tuple_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
    fields: usize,
) -> P<ast::Expr> {
    let value_ty = builder.ty()
        .ref_()
            .lifetime("'__a")
            .build_ty(serialize_ty);

    let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
        cx,
        builder,
        value_ty,
        fields,
        impl_generics,
    );

    let type_name = builder.expr().str(type_ident);

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_named_seq($type_name, Visitor {
            value: self,
            state: 0,
        })
    })
}

fn serialize_struct(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    impl_generics: &ast::Generics,
    serialize_ty: P<ast::Ty>,
    struct_def: &StructDef,
    fields: Vec<Ident>,
) -> P<ast::Expr> {
    let value_ty = builder.ty()
        .ref_()
            .lifetime("'__a")
            .build_ty(serialize_ty.clone());

    let (visitor_struct, visitor_impl) = serialize_struct_visitor(
        cx,
        builder,
        value_ty,
        struct_def,
        impl_generics,
        fields.iter().map(|field| quote_expr!(cx, &self.value.$field)),
    );

    let type_name = builder.expr().str(type_ident);

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_named_map($type_name, Visitor {
            value: self,
            state: 0,
        })
    })
}

fn serialize_item_enum(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    impl_generics: &ast::Generics,
    enum_def: &ast::EnumDef,
) -> P<ast::Expr> {
    let arms: Vec<ast::Arm> = enum_def.variants.iter()
        .map(|variant| {
            serialize_variant(
                cx,
                builder,
                type_ident,
                impl_generics,
                variant,
            )
        })
        .collect();

    quote_expr!(cx,
        match *self {
            $arms
        }
    )
}

fn serialize_variant(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_ident: Ident,
    generics: &ast::Generics,
    variant: &ast::Variant,
) -> ast::Arm {
    let type_name = builder.expr().str(type_ident);
    let variant_ident = variant.node.name;
    let variant_name = builder.expr().str(variant_ident);

    match variant.node.kind {
        ast::TupleVariantKind(ref args) if args.is_empty() => {
            let pat = builder.pat().enum_()
                .id(type_ident).id(variant_ident).build()
                .build();

            quote_arm!(cx,
                $pat => {
                    ::serde::ser::Serializer::visit_enum_unit(
                        serializer,
                        $type_name,
                        $variant_name,
                    )
                },
            )
        }
        ast::TupleVariantKind(ref args) => {
            let fields: Vec<ast::Ident> = (0 .. args.len())
                .map(|i| builder.id(format!("__field{}", i)))
                .collect();

            let pat = builder.pat().enum_()
                .id(type_ident).id(variant_ident).build()
                .with_pats(fields.iter().map(|field| builder.pat().ref_id(field)))
                .build();

            let expr = serialize_tuple_variant(
                cx,
                builder,
                type_name,
                variant_name,
                generics,
                args,
                fields,
            );

            quote_arm!(cx, $pat => { $expr })
        }
        ast::StructVariantKind(ref struct_def) => {
            let fields: Vec<_> = (0 .. struct_def.fields.len())
                .map(|i| builder.id(format!("__field{}", i)))
                .collect();

            let pat = builder.pat().struct_()
                .id(type_ident).id(variant_ident).build()
                .with_pats(
                    fields.iter()
                        .zip(struct_def.fields.iter())
                        .map(|(id, field)| {
                            let name = match field.node.kind {
                                ast::NamedField(name, _) => name,
                                ast::UnnamedField(_) => {
                                    cx.bug("struct variant has unnamed fields")
                                }
                            };

                            (name, builder.pat().ref_id(id))
                        })
                )
                .build();

            let expr = serialize_struct_variant(
                cx,
                builder,
                type_name,
                variant_name,
                generics,
                struct_def,
                fields,
            );

            quote_arm!(cx, $pat => { $expr })
        }
    }
}

fn serialize_tuple_variant(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_name: P<ast::Expr>,
    variant_name: P<ast::Expr>,
    generics: &ast::Generics,
    args: &[ast::VariantArg],
    fields: Vec<Ident>,
) -> P<ast::Expr> {
    let value_ty = builder.ty().tuple()
        .with_tys(
            args.iter().map(|arg| {
                builder.ty()
                    .ref_()
                    .lifetime("'__a")
                    .build_ty(arg.ty.clone())
            })
        )
        .build();

    let value_expr = builder.expr().tuple()
        .with_exprs(
            fields.iter().map(|field| {
                builder.expr()
                    .addr_of()
                    .id(field)
            })
        )
        .build();

    let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
        cx,
        builder,
        value_ty,
        args.len(),
        generics,
    );

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_enum_seq($type_name, $variant_name, Visitor {
            value: $value_expr,
            state: 0,
        })
    })
}

fn serialize_struct_variant(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    type_name: P<ast::Expr>,
    variant_name: P<ast::Expr>,
    generics: &ast::Generics,
    struct_def: &ast::StructDef,
    fields: Vec<Ident>,
) -> P<ast::Expr> {
    let value_ty = builder.ty().tuple()
        .with_tys(
            struct_def.fields.iter().map(|field| {
                builder.ty()
                    .ref_()
                    .lifetime("'__a")
                    .build_ty(field.node.ty.clone())
            })
        )
        .build();

    let value_expr = builder.expr().tuple()
        .with_exprs(
            fields.iter().map(|field| {
                builder.expr()
                    .addr_of()
                    .id(field)
            })
        )
        .build();

    let (visitor_struct, visitor_impl) = serialize_struct_visitor(
        cx,
        builder,
        value_ty,
        struct_def,
        generics,
        (0 .. fields.len()).map(|i| {
            builder.expr()
                .tup_field(i)
                .field("value").self_()
        })
    );

    quote_expr!(cx, {
        $visitor_struct
        $visitor_impl
        serializer.visit_enum_map($type_name, $variant_name, Visitor {
            value: $value_expr,
            state: 0,
        })
    })
}

fn serialize_tuple_struct_visitor(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    value_ty: P<ast::Ty>,
    fields: usize,
    generics: &ast::Generics
) -> (P<ast::Item>, P<ast::Item>) {
    let arms: Vec<ast::Arm> = (0 .. fields)
        .map(|i| {
            let first = builder.expr().bool(i == 0);
            let expr = builder.expr()
                .tup_field(i)
                .field("value").self_();

            quote_arm!(cx,
                $i => {
                    self.state += 1;
                    let v = try!(serializer.visit_seq_elt($first, &$expr));
                    Ok(Some(v))
                }
            )
        })
        .collect();

    let visitor_impl_generics = builder.from_generics(generics.clone())
        .add_lifetime_bound("'__a")
        .lifetime_name("'__a")
        .build();

    let where_clause = &visitor_impl_generics.where_clause;

    let visitor_generics = builder.from_generics(visitor_impl_generics.clone())
        .strip_bounds()
        .build();

    (
        quote_item!(cx,
            struct Visitor $visitor_impl_generics $where_clause {
                state: usize,
                value: $value_ty,
            }
        ).unwrap(),

        quote_item!(cx,
            impl $visitor_impl_generics ::serde::ser::SeqVisitor
            for Visitor $visitor_generics
            $where_clause {
                #[inline]
                fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
                    where S: ::serde::ser::Serializer,
                {
                    match self.state {
                        $arms
                        _ => Ok(None),
                    }
                }

                #[inline]
                fn len(&self) -> Option<usize> {
                    Some($fields)
                }
            }
        ).unwrap(),
    )
}

fn serialize_struct_visitor<I>(
    cx: &ExtCtxt,
    builder: &aster::AstBuilder,
    value_ty: P<ast::Ty>,
    struct_def: &StructDef,
    generics: &ast::Generics,
    value_exprs: I,
) -> (P<ast::Item>, P<ast::Item>)
    where I: Iterator<Item=P<ast::Expr>>,
{
    let len = struct_def.fields.len();

    let key_exprs = struct_def.fields.iter()
        .map(|field| {
            match field_alias(field) {
                Some(lit) => builder.expr().build_lit(P(lit.clone())),
                None => {
                    match field.node.kind {
                        ast::NamedField(name, _) => {
                            builder.expr().str(name)
                        }
                        ast::UnnamedField(_) => {
                            cx.bug("struct has named and unnamed fields")
                        }
                    }
                }
            }
        });

    let arms: Vec<ast::Arm> = key_exprs
        .zip(value_exprs)
        .enumerate()
        .map(|(i, (key_expr, value_expr))| {
            let first = i == 0;

            quote_arm!(cx,
                $i => {
                    self.state += 1;
                    Ok(
                        Some(
                            try!(
                                serializer.visit_map_elt(
                                    $first,
                                    $key_expr,
                                    $value_expr,
                                )
                            )
                        )
                    )
                }
            )
        })
        .collect();

    let visitor_impl_generics = builder.from_generics(generics.clone())
        .add_lifetime_bound("'__a")
        .lifetime_name("'__a")
        .build();

    let where_clause = &visitor_impl_generics.where_clause;

    let visitor_generics = builder.from_generics(visitor_impl_generics.clone())
        .strip_bounds()
        .build();

    (
        quote_item!(cx,
            struct Visitor $visitor_impl_generics $where_clause {
                state: usize,
                value: $value_ty,
            }
        ).unwrap(),

        quote_item!(cx,
            impl $visitor_impl_generics
            ::serde::ser::MapVisitor
            for Visitor $visitor_generics
            $where_clause {
                #[inline]
                fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
                    where S: ::serde::ser::Serializer,
                {
                    match self.state {
                        $arms
                        _ => Ok(None),
                    }
                }

                #[inline]
                fn len(&self) -> Option<usize> {
                    Some($len)
                }
            }
        ).unwrap(),
    )
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use std;
use common::*;
use endian::*;
use message::*;

pub mod InputStreamMessageReader {

    use std;
    use endian::*;
    use message::*;

    pub fn new<U : std::rt::io::Reader, T>(inputStream : &mut U,
                                           options : ReaderOptions,
                                           cont : &fn(v : &mut MessageReader) -> T) -> T {

        let firstWord = inputStream.read_bytes(8);

        let segmentCount : u32 =
            unsafe {let p : *WireValue<u32> = std::cast::transmute(firstWord.unsafe_ref(0));
                    (*p).get() + 1
                   };

        let segment0Size =
            if (segmentCount == 0) { 0 } else {
            unsafe {let p : *WireValue<u32> = std::cast::transmute(firstWord.unsafe_ref(4));
                    (*p).get()
                   }
            };

        let mut totalWords = segment0Size;

        if (segmentCount >= 512) {
            fail!("too many segments");
        }

        let mut moreSizes : ~[u32] = std::vec::from_elem((segmentCount & !1) as uint, 0u32);

        if (segmentCount > 1) {
            let moreSizesRaw = inputStream.read_bytes((4 * (segmentCount & !1)) as uint);
            for ii in range(0, segmentCount as uint - 1) {
                moreSizes[ii] = unsafe {
                    let p : *WireValue<u32> =
                        std::cast::transmute(moreSizesRaw.unsafe_ref(ii * 4));
                    (*p).get()
                };
                totalWords += moreSizes[ii];
            }
        }

        //# Don't accept a message which the receiver couldn't possibly
        //# traverse without hitting the traversal limit. Without this
        //# check, a malicious client could transmit a very large
        //# segment size to make the receiver allocate excessive space
        //# and possibly crash.
        assert!(totalWords as u64 <= options.traversalLimitInWords);

        // TODO Is this guaranteed to be word-aligned?
        let ownedSpace : ~[u8] = inputStream.read_bytes(8 * totalWords as uint);

        // TODO lazy reading like in capnp-c++. Is that possible
        // within the std::io::Reader interface?

        let segment0 : &[u8] = ownedSpace.slice(0, 8 * segment0Size as uint);

        let mut segments : ~[&[u8]] = ~[segment0];

        if (segmentCount > 1) {
            let mut offset = segment0Size;

            for ii in range(0, segmentCount as uint - 1) {
                segments.push(ownedSpace.slice(offset as uint * 8,
                                               (offset + moreSizes[ii]) as uint * 8));
                offset += moreSizes[ii];
            }
        }

        let mut result = ~MessageReader::<'a> {
            segments : segments,
            options : options
        };

        cont(result)

    }
}

pub fn writeMessage<T: std::rt::io::Writer>(outputStream : &mut T,
                                            message : &MessageBuilder) {

    let tableSize : uint = ((message.segments.len() + 2) & (!1));

    let mut table : ~[WireValue<u32>] = std::vec::with_capacity(tableSize);
    unsafe { std::vec::raw::set_len(&mut table, tableSize) }

    table[0].set((message.segments.len() - 1) as u32);

    for i in range(0, message.segments.len()) {
        table[i + 1].set(message.segmentBuilders[i].pos as u32);
    }
    if (message.segments.len() % 2 == 0) {
        // Set padding.
        table[message.segments.len() + 1].set( 0 );
    }

    unsafe {
        let ptr : *u8 = std::cast::transmute(table.unsafe_ref(0));
        do std::vec::raw::buf_as_slice::<u8,()>(ptr, table.len() * 4) |buf| {
            outputStream.write(buf);
        }
    }

    for i in range(0, message.segments.len()) {
        let slice = message.segments[i].slice(0, message.segmentBuilders[i].pos * BYTES_PER_WORD);
        outputStream.write(slice);
    }
}
<commit_msg>remove undeclared lifetime<commit_after>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use std;
use common::*;
use endian::*;
use message::*;

pub mod InputStreamMessageReader {

    use std;
    use endian::*;
    use message::*;

    pub fn new<U : std::rt::io::Reader, T>(inputStream : &mut U,
                                           options : ReaderOptions,
                                           cont : &fn(v : &mut MessageReader) -> T) -> T {

        let firstWord = inputStream.read_bytes(8);

        let segmentCount : u32 =
            unsafe {let p : *WireValue<u32> = std::cast::transmute(firstWord.unsafe_ref(0));
                    (*p).get() + 1
                   };

        let segment0Size =
            if (segmentCount == 0) { 0 } else {
            unsafe {let p : *WireValue<u32> = std::cast::transmute(firstWord.unsafe_ref(4));
                    (*p).get()
                   }
            };

        let mut totalWords = segment0Size;

        if (segmentCount >= 512) {
            fail!("too many segments");
        }

        let mut moreSizes : ~[u32] = std::vec::from_elem((segmentCount & !1) as uint, 0u32);

        if (segmentCount > 1) {
            let moreSizesRaw = inputStream.read_bytes((4 * (segmentCount & !1)) as uint);
            for ii in range(0, segmentCount as uint - 1) {
                moreSizes[ii] = unsafe {
                    let p : *WireValue<u32> =
                        std::cast::transmute(moreSizesRaw.unsafe_ref(ii * 4));
                    (*p).get()
                };
                totalWords += moreSizes[ii];
            }
        }

        //# Don't accept a message which the receiver couldn't possibly
        //# traverse without hitting the traversal limit. Without this
        //# check, a malicious client could transmit a very large
        //# segment size to make the receiver allocate excessive space
        //# and possibly crash.
        assert!(totalWords as u64 <= options.traversalLimitInWords);

        // TODO Is this guaranteed to be word-aligned?
        let ownedSpace : ~[u8] = inputStream.read_bytes(8 * totalWords as uint);

        // TODO lazy reading like in capnp-c++. Is that possible
        // within the std::io::Reader interface?

        let segment0 : &[u8] = ownedSpace.slice(0, 8 * segment0Size as uint);

        let mut segments : ~[&[u8]] = ~[segment0];

        if (segmentCount > 1) {
            let mut offset = segment0Size;

            for ii in range(0, segmentCount as uint - 1) {
                segments.push(ownedSpace.slice(offset as uint * 8,
                                               (offset + moreSizes[ii]) as uint * 8));
                offset += moreSizes[ii];
            }
        }

        let mut result = ~MessageReader {
            segments : segments,
            options : options
        };

        cont(result)

    }
}

pub fn writeMessage<T: std::rt::io::Writer>(outputStream : &mut T,
                                            message : &MessageBuilder) {

    let tableSize : uint = ((message.segments.len() + 2) & (!1));

    let mut table : ~[WireValue<u32>] = std::vec::with_capacity(tableSize);
    unsafe { std::vec::raw::set_len(&mut table, tableSize) }

    table[0].set((message.segments.len() - 1) as u32);

    for i in range(0, message.segments.len()) {
        table[i + 1].set(message.segmentBuilders[i].pos as u32);
    }
    if (message.segments.len() % 2 == 0) {
        // Set padding.
        table[message.segments.len() + 1].set( 0 );
    }

    unsafe {
        let ptr : *u8 = std::cast::transmute(table.unsafe_ref(0));
        do std::vec::raw::buf_as_slice::<u8,()>(ptr, table.len() * 4) |buf| {
            outputStream.write(buf);
        }
    }

    for i in range(0, message.segments.len()) {
        let slice = message.segments[i].slice(0, message.segmentBuilders[i].pos * BYTES_PER_WORD);
        outputStream.write(slice);
    }
}
<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2019 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! ## Summary
//! One of CITA's core components that processing blocks and transaction storage,
//! provides queries, caches query records, and more.
//!
//! ### Message queuing situation
//!
//! 1. Subscribe channel
//!
//!     | Queue   | PubModule   | Message Type     |
//!     | ------- | ----------- | ---------------- |
//!     | chain   | Net         | SyncResponse     |
//!     | chain   | Net         | SyncRequest      |
//!     | chain   | Consensus   | BlockWithProof   |
//!     | chain   | Jsonrpc     | Request          |
//!     | chain   | Auth        | BlockTxHashesReq |
//!     | chain   | Executor    | ExecutedResult   |
//!     | chain   | Snapshot    | SnapshotReq      |
//!     | chain   | Executor    | StateSignal      |
//!
//! 2. Publish channel
//!
//!     | Queue | PubModule | SubModule     | Message Type  |
//!     | ----- | --------- | ------------- | ------------- |
//!     | chain | Chain     | Auth          | BlockTxHashes |
//!     | chain | Chain     | Net           | Status        |
//!     | chain | Chain     | Executor      | Request       |
//!     | chain | Chain     | Executor      | StateSignal   |
//!     | chain | Chain     | Jsonrpc       | Response      |
//!     | chain | Chain     | Net           | SyncResponse  |
//!     | chain | Chain     | Snapshot      | SnapshotResp  |
//!     | chain | Chain     | Executor      | LocalSync     |
//!     | chain | Chain     | Consensus     | RichStatus    |
//!     | chain | Chain     | Executor      | RichStatus    |
//!
//! ### Key behavior
//!
//! the key struct:
//!
//! - [`Chain`]
//! - `Forward`: `forward::Forward`
//! - `BlockProcessor`: `block_processor::BlockProcessor`
//!
//! Construct a caching mechanism with `RowLock<Vec<.. >>` or `RowLock<HashMap<.. >>` and clean it regularly.
//!
//! `Forward` listen to the message bus, handle read commands or forward write commands according to message key.
//!
//! `BlockProcessor` processing according to the forwarded information.
//!
//! [`Chain`]: ../core/libchain/chain/struct.Chain.html
//!

extern crate common_types as types;
#[macro_use]
extern crate libproto;
#[macro_use]
extern crate cita_logger as logger;
#[macro_use]
extern crate util;

mod block_processor;
mod forward;

use crate::block_processor::BlockProcessor;
use crate::forward::Forward;
use cita_db::{Config as DatabaseConfig, RocksDB, NUM_COLUMNS};
use cita_directories::DataPath;
use clap::App;
use core::libchain;
use libproto::router::{MsgType, RoutingKey, SubModules};
use pubsub::{channel, start_pubsub};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use util::set_panic_handler;

include!(concat!(env!("OUT_DIR"), "/build_info.rs"));

fn main() {
    let matches = App::new("chain")
        .version(get_build_info_str(true))
        .long_version(get_build_info_str(false))
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .args_from_usage(
            "-c, --config=[FILE] 'Sets a chain config file'
                          -s, --stdout 'Log to console'",
        )
        .get_matches();

    let stdout = matches.is_present("stdout");
    micro_service_init!("cita-chain", "CITA:chain", stdout);
    info!("Version: {}", get_build_info_str(true));

    let config_path = matches.value_of("config").unwrap_or("chain.toml");

    let (tx, rx) = channel::unbounded();
    let (ctx_pub, crx_pub) = channel::unbounded();
    start_pubsub(
        "chain",
        routing_key!([
            Net >> SyncResponse,
            Net >> SyncRequest,
            Consensus >> BlockWithProof,
            Jsonrpc >> Request,
            Auth >> BlockTxHashesReq,
            Executor >> ExecutedResult,
            Executor >> StateSignal,
            Snapshot >> SnapshotReq,
        ]),
        tx,
        crx_pub,
    );

    let nosql_path = DataPath::nosql_path();
    trace!("nosql_path is {:?}", nosql_path);
    let db_config = DatabaseConfig::with_category_num(NUM_COLUMNS);
    // TODO Fix the unwrap
    let db = RocksDB::open(&nosql_path, &db_config).unwrap();

    let chain_config = libchain::chain::Config::new(config_path);
    let chain = Arc::new(libchain::chain::Chain::init_chain(
        Arc::new(db),
        chain_config,
    ));

    let (write_sender, write_receiver) = channel::unbounded();
    let forward = Forward::new(Arc::clone(&chain), ctx_pub.clone(), write_sender);

    let block_processor = BlockProcessor::new(Arc::clone(&chain), ctx_pub);

    // Two threads, one for reading, one for writing
    // Read: dispatch msg
    thread::spawn(move || loop {
        if let Ok((key, msg)) = rx.recv() {
            forward.dispatch_msg(&key, &msg);
        }
    });

    // Write: add block
    thread::spawn(move || {
        let mut timeout_factor = 0u8;
        loop {
            if let Ok(einfo) = write_receiver
                .recv_timeout(Duration::new(18 * (2u64.pow(u32::from(timeout_factor))), 0))
            {
                block_processor.set_executed_result(&einfo);
                timeout_factor = 0;
            } else if !*block_processor.chain.is_snapshot.read() {
                // Here will be these status:
                // 1. Executor process restarts, lost cached block information.
                // 2. Executor encountered an invalid block and cleared the block map.
                // 3. Bft restarted, lost chain status information, unable to consensus, unable to generate block.
                //
                // This will trigger:
                // 1. Network retransmits block information or initiates a synchronization request,
                //    and then the executor will receive a block message
                // 2. Bft will receive the latest status of chain
                info!("Chain enters the timeout retransmission phase");
                block_processor.reset_max_store_height();
                block_processor.signal_to_executor();
                block_processor.broadcast_current_status();
                if timeout_factor < 6 {
                    timeout_factor += 1
                }
            }
        }
    });
}
<commit_msg>Fix: main process exit in chain.<commit_after>// CITA
// Copyright 2016-2019 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any
// later version.

// This program is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! ## Summary
//! One of CITA's core components that processing blocks and transaction storage,
//! provides queries, caches query records, and more.
//!
//! ### Message queuing situation
//!
//! 1. Subscribe channel
//!
//!     | Queue   | PubModule   | Message Type     |
//!     | ------- | ----------- | ---------------- |
//!     | chain   | Net         | SyncResponse     |
//!     | chain   | Net         | SyncRequest      |
//!     | chain   | Consensus   | BlockWithProof   |
//!     | chain   | Jsonrpc     | Request          |
//!     | chain   | Auth        | BlockTxHashesReq |
//!     | chain   | Executor    | ExecutedResult   |
//!     | chain   | Snapshot    | SnapshotReq      |
//!     | chain   | Executor    | StateSignal      |
//!
//! 2. Publish channel
//!
//!     | Queue | PubModule | SubModule     | Message Type  |
//!     | ----- | --------- | ------------- | ------------- |
//!     | chain | Chain     | Auth          | BlockTxHashes |
//!     | chain | Chain     | Net           | Status        |
//!     | chain | Chain     | Executor      | Request       |
//!     | chain | Chain     | Executor      | StateSignal   |
//!     | chain | Chain     | Jsonrpc       | Response      |
//!     | chain | Chain     | Net           | SyncResponse  |
//!     | chain | Chain     | Snapshot      | SnapshotResp  |
//!     | chain | Chain     | Executor      | LocalSync     |
//!     | chain | Chain     | Consensus     | RichStatus    |
//!     | chain | Chain     | Executor      | RichStatus    |
//!
//! ### Key behavior
//!
//! the key struct:
//!
//! - [`Chain`]
//! - `Forward`: `forward::Forward`
//! - `BlockProcessor`: `block_processor::BlockProcessor`
//!
//! Construct a caching mechanism with `RowLock<Vec<.. >>` or `RowLock<HashMap<.. >>` and clean it regularly.
//!
//! `Forward` listen to the message bus, handle read commands or forward write commands according to message key.
//!
//! `BlockProcessor` processing according to the forwarded information.
//!
//! [`Chain`]: ../core/libchain/chain/struct.Chain.html
//!

extern crate common_types as types;
#[macro_use]
extern crate libproto;
#[macro_use]
extern crate cita_logger as logger;
#[macro_use]
extern crate util;

mod block_processor;
mod forward;

use crate::block_processor::BlockProcessor;
use crate::forward::Forward;
use cita_db::{Config as DatabaseConfig, RocksDB, NUM_COLUMNS};
use cita_directories::DataPath;
use clap::App;
use core::libchain;
use libproto::router::{MsgType, RoutingKey, SubModules};
use pubsub::{channel, start_pubsub};
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use util::set_panic_handler;

include!(concat!(env!("OUT_DIR"), "/build_info.rs"));

fn main() {
    let matches = App::new("chain")
        .version(get_build_info_str(true))
        .long_version(get_build_info_str(false))
        .author("Cryptape")
        .about("CITA Block Chain Node powered by Rust")
        .args_from_usage(
            "-c, --config=[FILE] 'Sets a chain config file'
                          -s, --stdout 'Log to console'",
        )
        .get_matches();

    let stdout = matches.is_present("stdout");
    micro_service_init!("cita-chain", "CITA:chain", stdout);
    info!("Version: {}", get_build_info_str(true));

    let config_path = matches.value_of("config").unwrap_or("chain.toml");

    let (tx, rx) = channel::unbounded();
    let (ctx_pub, crx_pub) = channel::unbounded();
    start_pubsub(
        "chain",
        routing_key!([
            Net >> SyncResponse,
            Net >> SyncRequest,
            Consensus >> BlockWithProof,
            Jsonrpc >> Request,
            Auth >> BlockTxHashesReq,
            Executor >> ExecutedResult,
            Executor >> StateSignal,
            Snapshot >> SnapshotReq,
        ]),
        tx,
        crx_pub,
    );

    let nosql_path = DataPath::nosql_path();
    trace!("nosql_path is {:?}", nosql_path);
    let db_config = DatabaseConfig::with_category_num(NUM_COLUMNS);
    // TODO Fix the unwrap
    let db = RocksDB::open(&nosql_path, &db_config).unwrap();

    let chain_config = libchain::chain::Config::new(config_path);
    let chain = Arc::new(libchain::chain::Chain::init_chain(
        Arc::new(db),
        chain_config,
    ));

    let (write_sender, write_receiver) = channel::unbounded();
    let forward = Forward::new(Arc::clone(&chain), ctx_pub.clone(), write_sender);

    let block_processor = BlockProcessor::new(Arc::clone(&chain), ctx_pub);

    // Two threads, one for reading, one for writing
    // Read: dispatch msg
    thread::spawn(move || loop {
        if let Ok((key, msg)) = rx.recv() {
            forward.dispatch_msg(&key, &msg);
        }
    });

    // Write: add block
    let mut timeout_factor = 0u8;
    loop {
        if let Ok(einfo) = write_receiver
            .recv_timeout(Duration::new(18 * (2u64.pow(u32::from(timeout_factor))), 0))
        {
            block_processor.set_executed_result(&einfo);
            timeout_factor = 0;
        } else if !*block_processor.chain.is_snapshot.read() {
            // Here will be these status:
            // 1. Executor process restarts, lost cached block information.
            // 2. Executor encountered an invalid block and cleared the block map.
            // 3. Bft restarted, lost chain status information, unable to consensus, unable to generate block.
            //
            // This will trigger:
            // 1. Network retransmits block information or initiates a synchronization request,
            //    and then the executor will receive a block message
            // 2. Bft will receive the latest status of chain
            info!("Chain enters the timeout retransmission phase");
            block_processor.reset_max_store_height();
            block_processor.signal_to_executor();
            block_processor.broadcast_current_status();
            if timeout_factor < 6 {
                timeout_factor += 1
            }
        }
    }
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;

use crate::driver::cmd::Args;
use crate::gc::bump::BumpAllocator;
use crate::gc::root::{determine_strong_roots, Slot};
use crate::gc::tlab;
use crate::gc::{
    formatted_size, iterate_weak_roots, Address, CollectionStats, Collector, GcReason, Region,
};
use crate::mem;
use crate::object::Obj;
use crate::os::{self, MemoryPermission};
use crate::safepoint;
use crate::timer::Timer;
use crate::vm::VM;

pub struct CopyCollector {
    total: Region,
    separator: Address,

    alloc: BumpAllocator,
    stats: Mutex<CollectionStats>,
}

impl CopyCollector {
    pub fn new(args: &Args) -> CopyCollector {
        let alignment = 2 * os::page_size();
        let heap_size = mem::align_usize(args.max_heap_size(), alignment);
        let heap_start = os::commit(heap_size, false);

        if heap_start.is_null() {
            panic!("could not allocate semi space of size {} bytes", heap_size);
        }

        let heap = heap_start.region_start(heap_size);

        let semi_size = heap_size / 2;
        let separator = heap_start.offset(semi_size);

        if args.flag_gc_verbose {
            println!("GC: {}; semi size: {}", heap, formatted_size(semi_size),);
        }

        CopyCollector {
            total: heap,
            separator,
            alloc: BumpAllocator::new(heap_start, separator),
            stats: Mutex::new(CollectionStats::new()),
        }
    }
}

impl Collector for CopyCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, vm: &VM, size: usize) -> Option<Region> {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return Some(ptr.region_start(size));
        }

        self.collect(vm, GcReason::AllocationFailure);

        let ptr = self.alloc.bump_alloc(size);

        return if ptr.is_null() {
            None
        } else {
            Some(ptr.region_start(size))
        };
    }

    fn alloc(&self, vm: &VM, size: usize, _array_ref: bool) -> Address {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return ptr;
        }

        self.collect(vm, GcReason::AllocationFailure);
        self.alloc.bump_alloc(size)
    }

    fn collect(&self, vm: &VM, reason: GcReason) {
        let mut timer = Timer::new(vm.args.flag_gc_stats);

        safepoint::stop_the_world(vm, |threads| {
            tlab::make_iterable_all(vm, &*threads);
            let rootset = determine_strong_roots(vm, &*threads);
            self.copy_collect(vm, &rootset, reason);
        });

        if vm.args.flag_gc_stats {
            let duration = timer.stop();
            let mut stats = self.stats.lock();
            stats.add(duration);
        }
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, runtime: f32) {
        let stats = self.stats.lock();
        let (mutator, gc) = stats.percentage(runtime);

        println!("GC stats: total={:.1}", runtime);
        println!("GC stats: mutator={:.1}", stats.mutator(runtime));
        println!("GC stats: collection={:.1}", stats.pause());

        println!("");
        println!("GC stats: collection-count={}", stats.collections());
        println!("GC stats: collection-pauses={}", stats.pauses());

        println!(
            "GC summary: {:.1}ms collection ({}), {:.1}ms mutator, {:.1}ms total ({}% mutator, {}% GC)",
            stats.pause(),
            stats.collections(),
            stats.mutator(runtime),
            runtime,
            mutator,
            gc,
        );
    }
}

impl Drop for CopyCollector {
    fn drop(&mut self) {
        os::free(self.total.start, self.total.size());
    }
}

impl CopyCollector {
    fn copy_collect(&self, vm: &VM, rootset: &[Slot], reason: GcReason) {
        let timer = Timer::new(vm.args.flag_gc_verbose);

        // enable writing into to-space again (for debug builds)
        if cfg!(debug_assertions) {
            let to_space = self.to_space();
            os::protect(to_space.start, to_space.size(), MemoryPermission::ReadWrite);
        }

        // empty to-space
        let to_space = self.to_space();
        let from_space = self.from_space();

        // determine size of heap before collection
        let old_size = self.alloc.top().offset_from(from_space.start);

        let mut top = to_space.start;
        let mut scan = top;

        for root in rootset {
            let root_ptr = root.get();

            if from_space.contains(root_ptr) {
                root.set(self.copy(root_ptr, &mut top));
            }
        }

        while scan < top {
            let object: &mut Obj = scan.to_mut_obj();

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if from_space.contains(field_ptr) {
                    field.set(self.copy(field_ptr, &mut top));
                }
            });

            scan = scan.offset(object.size());
        }

        self.iterate_weak_roots(vm);

        // disable access in current from-space
        // makes sure that no pointer into from-space is left (in debug-builds)
        if cfg!(debug_assertions) {
            os::protect(from_space.start, from_space.size(), MemoryPermission::None);
        }

        self.alloc.reset(top, to_space.end);

        timer.stop_with(|time_pause| {
            let new_size = top.offset_from(to_space.start);
            let garbage = old_size - new_size;
            let garbage_ratio = if old_size == 0 {
                0f64
            } else {
                (garbage as f64 / old_size as f64) * 100f64
            };

            println!(
                "Copy GC: {:.1} ms, {}->{} size, {}/{:.0}% garbage, ({})",
                time_pause,
                formatted_size(old_size),
                formatted_size(new_size),
                formatted_size(garbage),
                garbage_ratio,
                reason
            );
        });
    }

    fn iterate_weak_roots(&self, vm: &VM) {
        iterate_weak_roots(vm, |current_address| {
            debug_assert!(self.from_space().contains(current_address));
            let obj = current_address.to_mut_obj();

            if let Some(new_address) = obj.header().vtblptr_forwarded() {
                debug_assert!(self.to_space().contains(new_address));
                Some(new_address)
            } else {
                None
            }
        })
    }

    fn copy(&self, obj_addr: Address, top: &mut Address) -> Address {
        let obj = obj_addr.to_mut_obj();

        if let Some(fwd) = obj.header().vtblptr_forwarded() {
            return fwd;
        }

        let addr = *top;
        let obj_size = obj.size();

        obj.copy_to(addr, obj_size);
        *top = top.offset(obj_size);

        obj.header_mut().vtblptr_forward(addr);

        addr
    }

    pub fn from_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.total.start, self.separator)
        } else {
            Region::new(self.separator, self.total.end)
        }
    }

    pub fn to_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.separator, self.total.end)
        } else {
            Region::new(self.total.start, self.separator)
        }
    }
}
<commit_msg>move copy collector to iterate_strong_roots function<commit_after>use parking_lot::Mutex;

use std::sync::Arc;

use crate::driver::cmd::Args;
use crate::gc::bump::BumpAllocator;
use crate::gc::root::iterate_strong_roots;
use crate::gc::tlab;
use crate::gc::{
    formatted_size, iterate_weak_roots, Address, CollectionStats, Collector, GcReason, Region,
};
use crate::mem;
use crate::object::Obj;
use crate::os::{self, MemoryPermission};
use crate::safepoint;
use crate::threads::DoraThread;
use crate::timer::Timer;
use crate::vm::VM;

pub struct CopyCollector {
    total: Region,
    separator: Address,

    alloc: BumpAllocator,
    stats: Mutex<CollectionStats>,
}

impl CopyCollector {
    pub fn new(args: &Args) -> CopyCollector {
        let alignment = 2 * os::page_size();
        let heap_size = mem::align_usize(args.max_heap_size(), alignment);
        let heap_start = os::commit(heap_size, false);

        if heap_start.is_null() {
            panic!("could not allocate semi space of size {} bytes", heap_size);
        }

        let heap = heap_start.region_start(heap_size);

        let semi_size = heap_size / 2;
        let separator = heap_start.offset(semi_size);

        if args.flag_gc_verbose {
            println!("GC: {}; semi size: {}", heap, formatted_size(semi_size),);
        }

        CopyCollector {
            total: heap,
            separator,
            alloc: BumpAllocator::new(heap_start, separator),
            stats: Mutex::new(CollectionStats::new()),
        }
    }
}

impl Collector for CopyCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, vm: &VM, size: usize) -> Option<Region> {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return Some(ptr.region_start(size));
        }

        self.collect(vm, GcReason::AllocationFailure);

        let ptr = self.alloc.bump_alloc(size);

        return if ptr.is_null() {
            None
        } else {
            Some(ptr.region_start(size))
        };
    }

    fn alloc(&self, vm: &VM, size: usize, _array_ref: bool) -> Address {
        let ptr = self.alloc.bump_alloc(size);

        if ptr.is_non_null() {
            return ptr;
        }

        self.collect(vm, GcReason::AllocationFailure);
        self.alloc.bump_alloc(size)
    }

    fn collect(&self, vm: &VM, reason: GcReason) {
        let mut timer = Timer::new(vm.args.flag_gc_stats);

        safepoint::stop_the_world(vm, |threads| {
            tlab::make_iterable_all(vm, &*threads);
            self.copy_collect(vm, &*threads, reason);
        });

        if vm.args.flag_gc_stats {
            let duration = timer.stop();
            let mut stats = self.stats.lock();
            stats.add(duration);
        }
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, runtime: f32) {
        let stats = self.stats.lock();
        let (mutator, gc) = stats.percentage(runtime);

        println!("GC stats: total={:.1}", runtime);
        println!("GC stats: mutator={:.1}", stats.mutator(runtime));
        println!("GC stats: collection={:.1}", stats.pause());

        println!("");
        println!("GC stats: collection-count={}", stats.collections());
        println!("GC stats: collection-pauses={}", stats.pauses());

        println!(
            "GC summary: {:.1}ms collection ({}), {:.1}ms mutator, {:.1}ms total ({}% mutator, {}% GC)",
            stats.pause(),
            stats.collections(),
            stats.mutator(runtime),
            runtime,
            mutator,
            gc,
        );
    }
}

impl Drop for CopyCollector {
    fn drop(&mut self) {
        os::free(self.total.start, self.total.size());
    }
}

impl CopyCollector {
    fn copy_collect(&self, vm: &VM, threads: &[Arc<DoraThread>], reason: GcReason) {
        let timer = Timer::new(vm.args.flag_gc_verbose);

        // enable writing into to-space again (for debug builds)
        if cfg!(debug_assertions) {
            let to_space = self.to_space();
            os::protect(to_space.start, to_space.size(), MemoryPermission::ReadWrite);
        }

        // empty to-space
        let to_space = self.to_space();
        let from_space = self.from_space();

        // determine size of heap before collection
        let old_size = self.alloc.top().offset_from(from_space.start);

        let mut top = to_space.start;
        let mut scan = top;

        iterate_strong_roots(vm, threads, |root| {
            let root_ptr = root.get();

            if from_space.contains(root_ptr) {
                root.set(self.copy(root_ptr, &mut top));
            }
        });

        while scan < top {
            let object: &mut Obj = scan.to_mut_obj();

            object.visit_reference_fields(|field| {
                let field_ptr = field.get();

                if from_space.contains(field_ptr) {
                    field.set(self.copy(field_ptr, &mut top));
                }
            });

            scan = scan.offset(object.size());
        }

        self.iterate_weak_roots(vm);

        // disable access in current from-space
        // makes sure that no pointer into from-space is left (in debug-builds)
        if cfg!(debug_assertions) {
            os::protect(from_space.start, from_space.size(), MemoryPermission::None);
        }

        self.alloc.reset(top, to_space.end);

        timer.stop_with(|time_pause| {
            let new_size = top.offset_from(to_space.start);
            let garbage = old_size - new_size;
            let garbage_ratio = if old_size == 0 {
                0f64
            } else {
                (garbage as f64 / old_size as f64) * 100f64
            };

            println!(
                "Copy GC: {:.1} ms, {}->{} size, {}/{:.0}% garbage, ({})",
                time_pause,
                formatted_size(old_size),
                formatted_size(new_size),
                formatted_size(garbage),
                garbage_ratio,
                reason
            );
        });
    }

    fn iterate_weak_roots(&self, vm: &VM) {
        iterate_weak_roots(vm, |current_address| {
            debug_assert!(self.from_space().contains(current_address));
            let obj = current_address.to_mut_obj();

            if let Some(new_address) = obj.header().vtblptr_forwarded() {
                debug_assert!(self.to_space().contains(new_address));
                Some(new_address)
            } else {
                None
            }
        })
    }

    fn copy(&self, obj_addr: Address, top: &mut Address) -> Address {
        let obj = obj_addr.to_mut_obj();

        if let Some(fwd) = obj.header().vtblptr_forwarded() {
            return fwd;
        }

        let addr = *top;
        let obj_size = obj.size();

        obj.copy_to(addr, obj_size);
        *top = top.offset(obj_size);

        obj.header_mut().vtblptr_forward(addr);

        addr
    }

    pub fn from_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.total.start, self.separator)
        } else {
            Region::new(self.separator, self.total.end)
        }
    }

    pub fn to_space(&self) -> Region {
        if self.alloc.limit() == self.separator {
            Region::new(self.separator, self.total.end)
        } else {
            Region::new(self.total.start, self.separator)
        }
    }
}
<|endoftext|>


<commit_before>use std::time::Duration;

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};

// The benchmarks here verify that the complexity grows as O(*n*)
// where *n* is the number of characters in the text to be wrapped.

use lipsum::lipsum_words_from_seed;

const LINE_LENGTH: usize = 60;

/// Generate a lorem ipsum text with the given number of characters.
fn lorem_ipsum(length: usize) -> String {
    // The average word length in the lorem ipsum text is somewhere
    // between 6 and 7. So we conservatively divide by 5 to have a
    // long enough text that we can truncate below.
    let mut text = lipsum_words_from_seed(length / 5, 42);
    text.truncate(length);
    text
}

pub fn benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("String lengths");
    let lengths = [
        0, 5, 10, 20, 30, 40, 50, 60, 80, 100, 200, 300, 400, 600, 800, 1200, 1600, 2400, 3200,
        4800, 6400,
    ];
    for length in lengths {
        let text = lorem_ipsum(length);

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::UnicodeBreakProperties);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_unicode", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::FirstFit)
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_first_fit", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        group.bench_function(BenchmarkId::new("fill_inplace", length), |b| {
            b.iter_batched(
                || text.clone(),
                |mut text| textwrap::fill_inplace(&mut text, LINE_LENGTH),
                criterion::BatchSize::SmallInput,
            );
        });

        use hyphenation::{Language, Load, Standard};
        let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("la.standard.bincode");
        let dictionary = Standard::from_path(Language::Latin, &path).unwrap();
        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace)
            .word_splitter(textwrap::WordSplitter::Hyphenation(dictionary));
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii_hyphenation", length),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );
    }
    group.finish();
}

criterion_group!(
    name = benches;
    config = Criterion::default().warm_up_time(Duration::from_millis(500));
    targets = benchmark
);
criterion_main!(benches);
<commit_msg>Zero-pad benchmark IDs to make it easier to select benchmarks<commit_after>use std::time::Duration;

use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};

// The benchmarks here verify that the complexity grows as O(*n*)
// where *n* is the number of characters in the text to be wrapped.

use lipsum::lipsum_words_from_seed;

const LINE_LENGTH: usize = 60;

/// Generate a lorem ipsum text with the given number of characters.
fn lorem_ipsum(length: usize) -> String {
    // The average word length in the lorem ipsum text is somewhere
    // between 6 and 7. So we conservatively divide by 5 to have a
    // long enough text that we can truncate below.
    let mut text = lipsum_words_from_seed(length / 5, 42);
    text.truncate(length);
    text
}

pub fn benchmark(c: &mut Criterion) {
    let mut group = c.benchmark_group("String lengths");
    let lengths = [
        0, 5, 10, 20, 30, 40, 50, 60, 80, 100, 200, 300, 400, 600, 800, 1200, 1600, 2400, 3200,
        4800, 6400,
    ];
    for length in lengths {
        let text = lorem_ipsum(length);
        let length_id = format!("{length:04}");

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::UnicodeBreakProperties);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_unicode", &length_id),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii", &length_id),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::FirstFit)
            .word_separator(textwrap::WordSeparator::AsciiSpace);
        group.bench_with_input(
            BenchmarkId::new("fill_first_fit", &length_id),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );

        group.bench_function(BenchmarkId::new("fill_inplace", &length_id), |b| {
            b.iter_batched(
                || text.clone(),
                |mut text| textwrap::fill_inplace(&mut text, LINE_LENGTH),
                criterion::BatchSize::SmallInput,
            );
        });

        use hyphenation::{Language, Load, Standard};
        let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("la.standard.bincode");
        let dictionary = Standard::from_path(Language::Latin, &path).unwrap();
        let options = textwrap::Options::new(LINE_LENGTH)
            .wrap_algorithm(textwrap::WrapAlgorithm::new_optimal_fit())
            .word_separator(textwrap::WordSeparator::AsciiSpace)
            .word_splitter(textwrap::WordSplitter::Hyphenation(dictionary));
        group.bench_with_input(
            BenchmarkId::new("fill_optimal_fit_ascii_hyphenation", &length_id),
            &text,
            |b, text| {
                b.iter(|| textwrap::fill(text, &options));
            },
        );
    }
    group.finish();
}

criterion_group!(
    name = benches;
    config = Criterion::default().warm_up_time(Duration::from_millis(500));
    targets = benchmark
);
criterion_main!(benches);
<|endoftext|>


<commit_before>#![feature(test)]

extern crate calamine;
extern crate test;

use test::Bencher;
use calamine::{Xls, Xlsx, Xlsb, Ods, Reader, open_workbook};
use std::io::{BufReader};
use std::fs::File;

fn count<R: Reader<RS = BufReader<File>>>(path: &str) -> usize {
    let path = format!("{}/{}", env!("CARGO_MANIFEST_DIR"), path);
    let mut excel: R = open_workbook(&path).expect("cannot open excel file");

    let sheets = excel.sheet_names().to_owned();
    let mut count = 0;
    for s in sheets {
        count += excel
            .worksheet_range(&s)
            .unwrap()
            .unwrap()
            .rows()
            .flat_map(|r| r.iter())
            .count();
    }
    count
}

#[bench]
fn bench_xls(b: &mut Bencher) {
    b.iter(|| count::<Xls<_>>("tests/issues.xls"));
}

#[bench]
fn bench_xlsx(b: &mut Bencher) {
    b.iter(|| count::<Xlsx<_>>("tests/issues.xlsx"));
}

#[bench]
fn bench_xlsb(b: &mut Bencher) {
    b.iter(|| count::<Xlsb<_>>("tests/issues.xlsb"));
}

#[bench]
fn bench_ods(b: &mut Bencher) {
    b.iter(|| count::<Ods<_>>("tests/issues.ods"));
}
<commit_msg>rustfmt<commit_after>#![feature(test)]

extern crate calamine;
extern crate test;

use test::Bencher;
use calamine::{open_workbook, Ods, Reader, Xls, Xlsb, Xlsx};
use std::io::BufReader;
use std::fs::File;

fn count<R: Reader<RS = BufReader<File>>>(path: &str) -> usize {
    let path = format!("{}/{}", env!("CARGO_MANIFEST_DIR"), path);
    let mut excel: R = open_workbook(&path).expect("cannot open excel file");

    let sheets = excel.sheet_names().to_owned();
    let mut count = 0;
    for s in sheets {
        count += excel
            .worksheet_range(&s)
            .unwrap()
            .unwrap()
            .rows()
            .flat_map(|r| r.iter())
            .count();
    }
    count
}

#[bench]
fn bench_xls(b: &mut Bencher) {
    b.iter(|| count::<Xls<_>>("tests/issues.xls"));
}

#[bench]
fn bench_xlsx(b: &mut Bencher) {
    b.iter(|| count::<Xlsx<_>>("tests/issues.xlsx"));
}

#[bench]
fn bench_xlsb(b: &mut Bencher) {
    b.iter(|| count::<Xlsb<_>>("tests/issues.xlsb"));
}

#[bench]
fn bench_ods(b: &mut Bencher) {
    b.iter(|| count::<Ods<_>>("tests/issues.ods"));
}
<|endoftext|>