<commit_before>use quote::{quote, quote_spanned, ToTokens};
use syn::{
    braced, bracketed, parenthesized,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    spanned::Spanned,
    token, Attribute, Token,
};

pub fn generate_parse_module(code: &str) -> Result<proc_macro2::TokenStream, syn::Error> {
    let items: Items = syn::parse_str(code)?;
    Ok(Default::default())
}

pub struct Items {
    //pub use_stmts: Vec<SimpleUse>,
    pub items: Vec<Item>,
}

pub enum Item {
    Table(Table),
    Record(Record),
    Format(TableFormat),
    RawEnum(RawEnum),
    Flags(BitFlags),
}

#[derive(Debug, Clone)]
pub(crate) struct Table {
    //pub(crate) docs: Vec<syn::Attribute>,
    pub(crate) name: syn::Ident,
    pub fields: Fields,
}

#[derive(Debug, Clone)]
pub(crate) struct Record {
    //pub(crate) docs: Vec<syn::Attribute>,
    pub(crate) name: syn::Ident,
    pub fields: Fields,
}

/// A table with a format; we generate an enum
#[derive(Debug, Clone)]
pub(crate) struct TableFormat {
    pub(crate) docs: Vec<syn::Attribute>,
    pub(crate) name: syn::Ident,
    pub(crate) format: syn::Ident,
    pub(crate) variants: Vec<Variant>,
}

#[derive(Debug, Clone)]
pub(crate) struct Variant {
    pub docs: Vec<syn::Attribute>,
    pub name: syn::Ident,
    pub typ: syn::Ident,
}

#[derive(Debug, Clone)]
pub(crate) struct Fields {
    pub(crate) fields: Vec<Field>,
}

#[derive(Debug, Clone)]
pub(crate) struct Field {
    //pub(crate) docs: Vec<syn::Attribute>,
    //pub(crate) attrs: FieldAttrs,
    pub(crate) name: syn::Ident,
    pub(crate) typ: FieldType,
}

#[derive(Debug, Clone)]
pub(crate) struct FieldAttrs {}

#[derive(Debug, Clone)]
pub enum FieldType {
    Offset { typ: syn::Ident },
    Scalar { typ: syn::Ident },
    Other { typ: syn::Path },
    Array { inner_typ: Box<FieldType> },
}

/// A raw c-style enum
#[derive(Debug, Clone)]
pub(crate) struct RawEnum {
    pub(crate) docs: Vec<syn::Attribute>,
    pub(crate) name: syn::Ident,
    pub(crate) typ: syn::Ident,
    pub(crate) variants: Vec<RawVariant>,
}

/// A raw scalar variant
#[derive(Debug, Clone)]
pub(crate) struct RawVariant {
    pub(crate) docs: Vec<syn::Attribute>,
    pub(crate) name: syn::Ident,
    pub(crate) value: syn::LitInt,
}

/// A set of bit-flags
#[derive(Debug, Clone)]
pub(crate) struct BitFlags {
    pub(crate) docs: Vec<syn::Attribute>,
    pub(crate) name: syn::Ident,
    pub(crate) typ: syn::Ident,
    pub(crate) variants: Vec<RawVariant>,
}

mod kw {
    syn::custom_keyword!(table);
    syn::custom_keyword!(record);
    syn::custom_keyword!(flags);
    syn::custom_keyword!(format);
}

impl Parse for Items {
    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
        //let use_stmts = get_use_statements(input)?;
        let mut items = Vec::new();
        while !input.is_empty() {
            items.push(input.parse()?);
        }
        Ok(Self { items })
    }
}

impl Parse for Item {
    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
        let peek = input.fork();
        // skip attributes
        while peek.lookahead1().peek(Token![#]) {
            Attribute::parse_outer(&peek)?;
        }

        let lookahead = peek.lookahead1();
        if lookahead.peek(kw::table) {
            Ok(Self::Table(input.parse()?))
        } else if lookahead.peek(kw::record) {
            Ok(Self::Record(input.parse()?))
        } else if lookahead.peek(kw::flags) {
            Ok(Self::Flags(input.parse()?))
        } else if lookahead.peek(kw::format) {
            Ok(Self::Format(input.parse()?))
        } else if lookahead.peek(Token![enum]) {
            Ok(Self::RawEnum(input.parse()?))
        } else {
            Err(syn::Error::new(
                input.span(),
                "expected one of 'table' 'record' 'flags' 'format' or 'enum'.",
            ))
        }
    }
}

impl Parse for Table {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attributes = get_optional_attributes(input)?;
        let _table = input.parse::<kw::table>()?;
        let name = input.parse::<syn::Ident>()?;

        let fields = input.parse()?;
        Ok(Table { name, fields })
    }
}

impl Parse for Record {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let attributes = get_optional_attributes(input)?;
        let _kw = input.parse::<kw::record>()?;
        let name = input.parse::<syn::Ident>()?;

        let fields = input.parse()?;
        Ok(Record { name, fields })
    }
}

impl Parse for BitFlags {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let docs = get_optional_docs(input)?;
        let _kw = input.parse::<kw::flags>()?;
        let typ = input.parse::<syn::Ident>()?;
        validate_ident(&typ, &["u8", "u16"], "allowed bitflag types: u8, u16")?;
        let name = input.parse::<syn::Ident>()?;

        let content;
        let _ = braced!(content in input);
        let variants = Punctuated::<RawVariant, Token![,]>::parse_terminated(&content)?
            .into_iter()
            .collect();

        Ok(BitFlags {
            docs,
            name,
            typ,
            variants,
        })
    }
}

impl Parse for RawEnum {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let docs = get_optional_docs(input)?;
        let _kw = input.parse::<Token![enum]>()?;
        let typ = input.parse::<syn::Ident>()?;
        validate_ident(&typ, &["u8", "u16"], "allowed enum types: u8, u16")?;
        let name = input.parse::<syn::Ident>()?;
        let content;
        let _ = braced!(content in input);
        let variants = Punctuated::<RawVariant, Token![,]>::parse_terminated(&content)?
            .into_iter()
            .collect();
        Ok(RawEnum {
            docs,
            name,
            typ,
            variants,
        })
    }
}

impl Parse for TableFormat {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        //let attributes = get_optional_attributes(input)?;
        let docs = get_optional_docs(input)?;
        let _kw = input.parse::<kw::format>()?;
        let format: syn::Ident = input.parse()?;
        validate_ident(&format, &["u16"], "unexpected format type")?;
        let name = input.parse::<syn::Ident>()?;

        let content;
        let _ = braced!(content in input);
        let variants = Punctuated::<Variant, Token![,]>::parse_terminated(&content)?
            .into_iter()
            .collect();

        Ok(TableFormat {
            docs,
            format,
            name,
            variants,
        })
    }
}

impl Parse for Fields {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let content;
        let _ = braced!(content in input);
        let fields = Punctuated::<Field, Token![,]>::parse_terminated(&content)?
            .into_iter()
            .collect();
        Ok(Self { fields })
    }
}

impl Parse for Field {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let _attrs = get_optional_attributes(input)?;
        let name = input.parse::<syn::Ident>()?;
        let _ = input.parse::<Token![:]>()?;
        let typ = input.parse()?;
        Ok(Field { name, typ })
    }
}

impl Parse for FieldType {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        if input.lookahead1().peek(token::Bracket) {
            let content;
            bracketed!(content in input);
            let span = content.span();
            let inner_typ: FieldType = content.parse()?;
            if matches!(inner_typ, FieldType::Array { .. }) {
                return Err(syn::Error::new(span, "nested arrays are invalid"));
            }
            return Ok(FieldType::Array {
                inner_typ: Box::new(inner_typ),
            });
        }

        let path = input.parse::<syn::Path>()?;
        let last = path.segments.last().expect("do zero-length paths exist?");
        if last.ident != "BigEndian" {
            return Ok(FieldType::Other { typ: path.clone() });
        }

        let inner = get_single_generic_type_arg(&last.arguments).ok_or_else(|| {
            syn::Error::new(last.ident.span(), "expected single generic type argument")
        })?;
        let last = inner.segments.last().unwrap();
        if ["Offset16", "Offset24", "Offset32"].contains(&last.ident.to_string().as_str()) {
            Ok(FieldType::Offset {
                typ: last.ident.clone(),
            })
        } else if last.arguments.is_empty() {
            Ok(FieldType::Scalar {
                typ: last.ident.clone(),
            })
        } else {
            Err(syn::Error::new(last.span(), "unexpected arguments"))
        }
    }
}

impl Parse for RawVariant {
    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
        let docs = get_optional_docs(input)?;
        let name = input.parse::<syn::Ident>()?;
        let _ = input.parse::<Token![=]>()?;
        let value: syn::LitInt = input.parse()?;
        Ok(Self { docs, name, value })
    }
}

impl Parse for Variant {
    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
        let docs = get_optional_docs(input)?;
        let name = input.parse::<syn::Ident>()?;
        let content;
        parenthesized!(content in input);
        let typ = content.parse::<syn::Ident>()?;
        Ok(Self { docs, name, typ })
    }
}

fn validate_ident(ident: &syn::Ident, expected: &[&str], error: &str) -> Result<(), syn::Error> {
    if !expected.iter().any(|exp| ident == exp) {
        return Err(syn::Error::new(ident.span(), error));
    }
    Ok(())
}

fn get_optional_attributes(input: ParseStream) -> Result<Vec<syn::Attribute>, syn::Error> {
    let mut result = Vec::new();
    while input.lookahead1().peek(Token![#]) {
        result.extend(Attribute::parse_outer(input)?);
    }
    Ok(result)
}

fn get_optional_docs(input: ParseStream) -> Result<Vec<syn::Attribute>, syn::Error> {
    let mut result = Vec::new();
    while input.lookahead1().peek(Token![#]) {
        result.extend(Attribute::parse_outer(input)?);
    }
    for attr in &result {
        if !attr.path.is_ident("doc") {
            return Err(syn::Error::new(attr.span(), "expected doc comment"));
        }
    }
    Ok(result)
}

fn get_single_generic_type_arg(input: &syn::PathArguments) -> Option<syn::Path> {
    match input {
        syn::PathArguments::AngleBracketed(args) if args.args.len() == 1 => {
            let arg = args.args.last().unwrap();
            if let syn::GenericArgument::Type(syn::Type::Path(path)) = arg {
                if path.qself.is_none() && path.path.segments.len() == 1 {
                    return Some(path.path.clone());
                }
            }
            None
        }
        _ => None,
    }
}
<commit_msg>[codegen]: parse2 attribute parsing checkpoint<commit_after>use quote::{quote, quote_spanned, ToTokens};
use syn::{
    braced, bracketed, parenthesized,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    spanned::Spanned,
    token, Attribute, Token,
};

use parse::{Item, Items, Record, Table, TableFormat};

pub fn generate_parse_module(code: &str) -> Result<proc_macro2::TokenStream, syn::Error> {
    let items: Items = syn::parse_str(code)?;
    let mut code = Vec::new();
    for item in &items.items {
        let item_code = match item {
            Item::Table(item) => generate_table_impl(item)?,
            Item::Record(item) => generate_record_impl(item)?,
            //Item::Format(item) => todo!(),
            //Item::RawEnum(item) => todo!(),
            //Item::Flags(item) => todo!(),
            _ => Default::default(),
        };
        code.push(item_code);
    }

    //let use_stmts = &items.use_stmts;
    Ok(quote! {
        //#(#use_stmts)*
        #[allow(unused_imports)]
        use font_types::*;
        #(#code)*
    })
}

fn generate_table_impl(item: &Table) -> syn::Result<proc_macro2::TokenStream> {
    Ok(Default::default())
    //todo!()
}

fn generate_record_impl(item: &Record) -> syn::Result<proc_macro2::TokenStream> {
    let name = &item.name;
    let field_names = item.fields.iter().map(|fld| &fld.name);
    Ok(Default::default())
    //let field_docs = item.fields.iter().map(|fld| &fld.)
}

mod parse {

    use proc_macro2::{TokenStream, TokenTree};
    use syn::{
        braced, bracketed, parenthesized,
        parse::{Parse, ParseStream},
        punctuated::Punctuated,
        spanned::Spanned,
        token, Attribute, Token,
    };

    pub struct Items {
        //pub use_stmts: Vec<SimpleUse>,
        pub(crate) items: Vec<Item>,
    }

    pub enum Item {
        Table(Table),
        Record(Record),
        Format(TableFormat),
        RawEnum(RawEnum),
        Flags(BitFlags),
    }

    #[derive(Debug, Clone)]
    pub(crate) struct Table {
        //pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) name: syn::Ident,
        pub fields: Fields,
    }

    #[derive(Debug, Clone)]
    pub(crate) struct Record {
        //pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) name: syn::Ident,
        pub fields: Fields,
    }

    /// A table with a format; we generate an enum
    #[derive(Debug, Clone)]
    pub(crate) struct TableFormat {
        pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) name: syn::Ident,
        pub(crate) format: syn::Ident,
        pub(crate) variants: Vec<Variant>,
    }

    #[derive(Debug, Clone)]
    pub(crate) struct Variant {
        pub docs: Vec<syn::Attribute>,
        pub name: syn::Ident,
        pub typ: syn::Ident,
    }

    #[derive(Debug, Clone)]
    pub(crate) struct Fields {
        pub(crate) fields: Vec<Field>,
    }

    #[derive(Debug, Clone)]
    pub(crate) struct Field {
        //pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) attrs: FieldAttrs,
        pub(crate) name: syn::Ident,
        pub(crate) typ: FieldType,
    }

    #[derive(Debug, Default, Clone)]
    pub(crate) struct FieldAttrs {
        pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) nullable: Option<syn::Path>,
        pub(crate) available: Option<syn::Path>,
        pub(crate) no_getter: Option<syn::Path>,
        pub(crate) format: Option<FormatAttr>,
        pub(crate) count: Option<Count>,
    }

    #[derive(Debug, Clone)]
    pub(crate) struct FormatAttr {
        kw: syn::Ident,
        pub(crate) value: syn::LitInt,
    }

    /// Annotations for how to calculate the count of an array.
    #[derive(Debug, Clone)]
    pub(crate) enum Count {
        //Field(syn::Ident),
        Literal(syn::LitInt),
        All(syn::Path),
        Expr(TokenTree),
        //Function {
        //fn_: syn::Path,
        //args: Vec<syn::Ident>,
        //},
    }

    #[derive(Debug, Clone)]
    pub enum FieldType {
        Offset { typ: syn::Ident },
        Scalar { typ: syn::Ident },
        Other { typ: syn::Path },
        Array { inner_typ: Box<FieldType> },
    }

    /// A raw c-style enum
    #[derive(Debug, Clone)]
    pub(crate) struct RawEnum {
        pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) name: syn::Ident,
        pub(crate) typ: syn::Ident,
        pub(crate) variants: Vec<RawVariant>,
    }

    /// A raw scalar variant
    #[derive(Debug, Clone)]
    pub(crate) struct RawVariant {
        pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) name: syn::Ident,
        pub(crate) value: syn::LitInt,
    }

    /// A set of bit-flags
    #[derive(Debug, Clone)]
    pub(crate) struct BitFlags {
        pub(crate) docs: Vec<syn::Attribute>,
        pub(crate) name: syn::Ident,
        pub(crate) typ: syn::Ident,
        pub(crate) variants: Vec<RawVariant>,
    }

    impl Fields {
        pub(crate) fn iter(&self) -> impl Iterator<Item = &Field> {
            self.fields.iter()
        }
    }

    mod kw {
        syn::custom_keyword!(table);
        syn::custom_keyword!(record);
        syn::custom_keyword!(flags);
        syn::custom_keyword!(format);
        syn::custom_keyword!(all);
    }

    impl Parse for Items {
        fn parse(input: ParseStream) -> Result<Self, syn::Error> {
            //let use_stmts = get_use_statements(input)?;
            let mut items = Vec::new();
            while !input.is_empty() {
                items.push(input.parse()?);
            }
            Ok(Self { items })
        }
    }

    impl Parse for Item {
        fn parse(input: ParseStream) -> Result<Self, syn::Error> {
            let peek = input.fork();
            // skip attributes
            while peek.lookahead1().peek(Token![#]) {
                Attribute::parse_outer(&peek)?;
            }

            let lookahead = peek.lookahead1();
            if lookahead.peek(kw::table) {
                Ok(Self::Table(input.parse()?))
            } else if lookahead.peek(kw::record) {
                Ok(Self::Record(input.parse()?))
            } else if lookahead.peek(kw::flags) {
                Ok(Self::Flags(input.parse()?))
            } else if lookahead.peek(kw::format) {
                Ok(Self::Format(input.parse()?))
            } else if lookahead.peek(Token![enum]) {
                Ok(Self::RawEnum(input.parse()?))
            } else {
                Err(syn::Error::new(
                    input.span(),
                    "expected one of 'table' 'record' 'flags' 'format' or 'enum'.",
                ))
            }
        }
    }

    impl Parse for Table {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            let attributes = get_optional_attributes(input)?;
            let _table = input.parse::<kw::table>()?;
            let name = input.parse::<syn::Ident>()?;

            let fields = input.parse()?;
            Ok(Table { name, fields })
        }
    }

    impl Parse for Record {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            let attributes = get_optional_attributes(input)?;
            let _kw = input.parse::<kw::record>()?;
            let name = input.parse::<syn::Ident>()?;

            let fields = input.parse()?;
            Ok(Record { name, fields })
        }
    }

    impl Parse for BitFlags {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            let docs = get_optional_docs(input)?;
            let _kw = input.parse::<kw::flags>()?;
            let typ = input.parse::<syn::Ident>()?;
            validate_ident(&typ, &["u8", "u16"], "allowed bitflag types: u8, u16")?;
            let name = input.parse::<syn::Ident>()?;

            let content;
            let _ = braced!(content in input);
            let variants = Punctuated::<RawVariant, Token![,]>::parse_terminated(&content)?
                .into_iter()
                .collect();

            Ok(BitFlags {
                docs,
                name,
                typ,
                variants,
            })
        }
    }

    impl Parse for RawEnum {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            let docs = get_optional_docs(input)?;
            let _kw = input.parse::<Token![enum]>()?;
            let typ = input.parse::<syn::Ident>()?;
            validate_ident(&typ, &["u8", "u16"], "allowed enum types: u8, u16")?;
            let name = input.parse::<syn::Ident>()?;
            let content;
            let _ = braced!(content in input);
            let variants = Punctuated::<RawVariant, Token![,]>::parse_terminated(&content)?
                .into_iter()
                .collect();
            Ok(RawEnum {
                docs,
                name,
                typ,
                variants,
            })
        }
    }

    impl Parse for TableFormat {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            //let attributes = get_optional_attributes(input)?;
            let docs = get_optional_docs(input)?;
            let _kw = input.parse::<kw::format>()?;
            let format: syn::Ident = input.parse()?;
            validate_ident(&format, &["u16"], "unexpected format type")?;
            let name = input.parse::<syn::Ident>()?;

            let content;
            let _ = braced!(content in input);
            let variants = Punctuated::<Variant, Token![,]>::parse_terminated(&content)?
                .into_iter()
                .collect();

            Ok(TableFormat {
                docs,
                format,
                name,
                variants,
            })
        }
    }

    impl Parse for Fields {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            let content;
            let _ = braced!(content in input);
            let fields = Punctuated::<Field, Token![,]>::parse_terminated(&content)?
                .into_iter()
                .collect();
            Ok(Self { fields })
        }
    }

    impl Parse for Field {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            //let _attrs = get_optional_attributes(input)?;
            let attrs = input.parse()?;
            let name = input.parse::<syn::Ident>()?;
            let _ = input.parse::<Token![:]>()?;
            let typ = input.parse()?;
            Ok(Field { attrs, name, typ })
        }
    }

    impl Parse for FieldType {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            if input.lookahead1().peek(token::Bracket) {
                let content;
                bracketed!(content in input);
                let span = content.span();
                let inner_typ: FieldType = content.parse()?;
                if matches!(inner_typ, FieldType::Array { .. }) {
                    return Err(syn::Error::new(span, "nested arrays are invalid"));
                }
                return Ok(FieldType::Array {
                    inner_typ: Box::new(inner_typ),
                });
            }

            let path = input.parse::<syn::Path>()?;
            let last = path.segments.last().expect("do zero-length paths exist?");
            if last.ident != "BigEndian" {
                return Ok(FieldType::Other { typ: path.clone() });
            }

            let inner = get_single_generic_type_arg(&last.arguments).ok_or_else(|| {
                syn::Error::new(last.ident.span(), "expected single generic type argument")
            })?;
            let last = inner.segments.last().unwrap();
            if ["Offset16", "Offset24", "Offset32"].contains(&last.ident.to_string().as_str()) {
                Ok(FieldType::Offset {
                    typ: last.ident.clone(),
                })
            } else if last.arguments.is_empty() {
                Ok(FieldType::Scalar {
                    typ: last.ident.clone(),
                })
            } else {
                Err(syn::Error::new(last.span(), "unexpected arguments"))
            }
        }
    }

    impl Parse for RawVariant {
        fn parse(input: ParseStream) -> Result<Self, syn::Error> {
            let docs = get_optional_docs(input)?;
            let name = input.parse::<syn::Ident>()?;
            let _ = input.parse::<Token![=]>()?;
            let value: syn::LitInt = input.parse()?;
            Ok(Self { docs, name, value })
        }
    }

    impl Parse for Variant {
        fn parse(input: ParseStream) -> Result<Self, syn::Error> {
            let docs = get_optional_docs(input)?;
            let name = input.parse::<syn::Ident>()?;
            let content;
            parenthesized!(content in input);
            let typ = content.parse::<syn::Ident>()?;
            Ok(Self { docs, name, typ })
        }
    }

    static DOC: &str = "doc";
    static NULLABLE: &str = "nullable";
    static NO_GETTER: &str = "no_getter";
    static COUNT: &str = "count";
    static LEN: &str = "len";
    static COMPUTE_COUNT: &str = "compute_count";
    static AVAILABLE: &str = "available";
    static FORMAT: &str = "format";

    impl Parse for FieldAttrs {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            let mut this = FieldAttrs::default();
            let attrs = Attribute::parse_outer(input)
                .map_err(|e| syn::Error::new(e.span(), format!("hmm: '{e}'")))?;

            for attr in attrs {
                let ident = attr.path.get_ident().ok_or_else(|| {
                    syn::Error::new(attr.path.span(), "attr paths should be a single identifer")
                })?;
                if ident == DOC {
                    this.docs.push(attr);
                } else if ident == NULLABLE {
                    this.nullable = Some(attr.path);
                } else if ident == NO_GETTER {
                    this.no_getter = Some(attr.path);
                } else if ident == COUNT {
                    this.count = Some(attr.parse_args()?);
                } else if ident == AVAILABLE {
                } else if ident == COMPUTE_COUNT {
                } else if ident == LEN {
                } else if ident == FORMAT {
                    this.format = Some(FormatAttr {
                        kw: ident.clone(),
                        value: parse_attr_eq_value(attr.tokens)?,
                    });
                } else {
                    return Err(syn::Error::new(
                        ident.span(),
                        format!("unknown field attribute {ident}"),
                    ));
                }
            }
            Ok(this)
        }
    }

    impl Parse for Count {
        fn parse(input: ParseStream) -> syn::Result<Self> {
            let fork = input.fork();
            if let Ok(int) = fork.parse::<syn::LitInt>() {
                if input.is_empty() {
                    return Ok(Count::Literal(int));
                }
            }
            Ok(Count::Expr(input.parse()?))
        }
    }

    fn parse_attr_eq_value<T: Parse>(tokens: TokenStream) -> syn::Result<T> {
        /// the tokens '= T' where 'T' is any `Parse`
        struct EqualsThing<T>(T);

        impl<T: Parse> Parse for EqualsThing<T> {
            fn parse(input: ParseStream) -> syn::Result<Self> {
                input.parse::<Token![=]>()?;
                input.parse().map(EqualsThing)
            }
        }
        syn::parse2::<EqualsThing<T>>(tokens).map(|t| t.0)
    }

    fn validate_ident(
        ident: &syn::Ident,
        expected: &[&str],
        error: &str,
    ) -> Result<(), syn::Error> {
        if !expected.iter().any(|exp| ident == exp) {
            return Err(syn::Error::new(ident.span(), error));
        }
        Ok(())
    }

    fn get_optional_attributes(input: ParseStream) -> Result<Vec<syn::Attribute>, syn::Error> {
        let mut result = Vec::new();
        while input.lookahead1().peek(Token![#]) {
            result.extend(Attribute::parse_outer(input)?);
        }
        Ok(result)
    }

    fn get_optional_docs(input: ParseStream) -> Result<Vec<syn::Attribute>, syn::Error> {
        let mut result = Vec::new();
        while input.lookahead1().peek(Token![#]) {
            result.extend(Attribute::parse_outer(input)?);
        }
        for attr in &result {
            if !attr.path.is_ident("doc") {
                return Err(syn::Error::new(attr.span(), "expected doc comment"));
            }
        }
        Ok(result)
    }

    fn get_single_generic_type_arg(input: &syn::PathArguments) -> Option<syn::Path> {
        match input {
            syn::PathArguments::AngleBracketed(args) if args.args.len() == 1 => {
                let arg = args.args.last().unwrap();
                if let syn::GenericArgument::Type(syn::Type::Path(path)) = arg {
                    if path.qself.is_none() && path.path.segments.len() == 1 {
                        return Some(path.path.clone());
                    }
                }
                None
            }
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use crate::cmp;
use crate::ffi::c_void;
use crate::mem;
use crate::ptr;
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use crate::sys::hermit::abi;
use crate::sys::mutex::Mutex;
use crate::time::Duration;

// The implementation is inspired by Andrew D. Birrell's paper
// "Implementing Condition Variables with Semaphores"

pub struct Condvar {
    counter: AtomicUsize,
    sem1: *const c_void,
    sem2: *const c_void,
}

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

impl Condvar {
    pub const fn new() -> Condvar {
        Condvar { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() }
    }

    pub unsafe fn init(&mut self) {
        let _ = abi::sem_init(&mut self.sem1 as *mut *const c_void, 0);
        let _ = abi::sem_init(&mut self.sem2 as *mut *const c_void, 0);
    }

    pub unsafe fn notify_one(&self) {
        if self.counter.load(SeqCst) > 0 {
            self.counter.fetch_sub(1, SeqCst);
            abi::sem_post(self.sem1);
            abi::sem_timedwait(self.sem2, 0);
        }
    }

    pub unsafe fn notify_all(&self) {
        let counter = self.counter.swap(0, SeqCst);
        for _ in 0..counter {
            abi::sem_post(self.sem1);
        }
        for _ in 0..counter {
            abi::sem_timedwait(self.sem2, 0);
        }
    }

    pub unsafe fn wait(&self, mutex: &Mutex) {
        self.counter.fetch_add(1, SeqCst);
        mutex.unlock();
        abi::sem_timedwait(self.sem1, 0);
        abi::sem_post(self.sem2);
        mutex.lock();
    }

    pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
        panic!("wait_timeout not supported on hermit");
    }

    pub unsafe fn destroy(&self) {
        let _ = abi::sem_destroy(self.sem1);
        let _ = abi::sem_destroy(self.sem2);
    }
}
<commit_msg>remove some compiler warnings<commit_after>use crate::ffi::c_void;
use crate::ptr;
use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use crate::sys::hermit::abi;
use crate::sys::mutex::Mutex;
use crate::time::Duration;

// The implementation is inspired by Andrew D. Birrell's paper
// "Implementing Condition Variables with Semaphores"

pub struct Condvar {
    counter: AtomicUsize,
    sem1: *const c_void,
    sem2: *const c_void,
}

unsafe impl Send for Condvar {}
unsafe impl Sync for Condvar {}

impl Condvar {
    pub const fn new() -> Condvar {
        Condvar { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() }
    }

    pub unsafe fn init(&mut self) {
        let _ = abi::sem_init(&mut self.sem1 as *mut *const c_void, 0);
        let _ = abi::sem_init(&mut self.sem2 as *mut *const c_void, 0);
    }

    pub unsafe fn notify_one(&self) {
        if self.counter.load(SeqCst) > 0 {
            self.counter.fetch_sub(1, SeqCst);
            abi::sem_post(self.sem1);
            abi::sem_timedwait(self.sem2, 0);
        }
    }

    pub unsafe fn notify_all(&self) {
        let counter = self.counter.swap(0, SeqCst);
        for _ in 0..counter {
            abi::sem_post(self.sem1);
        }
        for _ in 0..counter {
            abi::sem_timedwait(self.sem2, 0);
        }
    }

    pub unsafe fn wait(&self, mutex: &Mutex) {
        self.counter.fetch_add(1, SeqCst);
        mutex.unlock();
        abi::sem_timedwait(self.sem1, 0);
        abi::sem_post(self.sem2);
        mutex.lock();
    }

    pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool {
        panic!("wait_timeout not supported on hermit");
    }

    pub unsafe fn destroy(&self) {
        let _ = abi::sem_destroy(self.sem1);
        let _ = abi::sem_destroy(self.sem2);
    }
}
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate clap;
#[macro_use]
extern crate log;

extern crate exonum;
extern crate blockchain_explorer;
extern crate configuration_service;

use clap::App;

use exonum::blockchain::{Blockchain, Service};
use exonum::node::Node;
use blockchain_explorer::helpers::{GenerateCommand, RunCommand};

use configuration_service::ConfigurationService;

fn main() {
    exonum::crypto::init();
    blockchain_explorer::helpers::init_logger().unwrap();

    let app = App::new("Simple configuration service demo program")
        .version(env!("CARGO_PKG_VERSION"))
        .author("Aleksey S. <aleksei.sidorov@xdev.re>")
        .subcommand(GenerateCommand::new())
        .subcommand(RunCommand::new());
    let matches = app.get_matches();

    match matches.subcommand() {
        ("generate", Some(matches)) => GenerateCommand::execute(matches),
        ("run", Some(matches)) => {
            let node_cfg = RunCommand::node_config(matches);
            let db = RunCommand::db(matches);

            let services: Vec<Box<Service>> = vec![Box::new(ConfigurationService::new())];
            let blockchain = Blockchain::new(db, services);
            Node::new(blockchain, node_cfg).run().unwrap();
        }
        _ => {
            panic!("Wrong subcommand");
        }
    }
}<commit_msg>Example moved to explorer<commit_after><|endoftext|>


<commit_before>//! Test loading of effect definitions from the disk.

extern crate libfriendship;
extern crate digest;
extern crate serde_json;
extern crate sha2;
extern crate tempdir;
extern crate url;

use std::fs::File;
use std::sync::mpsc::{channel, Receiver, Sender};

use digest::digest_reader;
use sha2::Sha256;
use tempdir::TempDir;
use url::Url;

use libfriendship::{Dispatch, Client};
use libfriendship::dispatch::{OscRouteGraph, OscRenderer, OscResMan};
use libfriendship::render::RefRenderer;
use libfriendship::routing::{adjlist, NodeHandle, DagHandle, Edge, EdgeWeight, EffectId, EffectDesc, EffectMeta};
use libfriendship::routing::AdjList;
use libfriendship::util::pack_f32;

struct MyClient {
    /// Where to send the rendered audio.
    tx: Sender<Vec<f32>>,
}
impl Client for MyClient {
    fn audio_rendered(&mut self, buffer: &[f32], _idx: u64, _num_slots: u32) {
        self.tx.send(buffer.iter().cloned().collect()).unwrap();
    }
}

fn test_setup() -> (Dispatch<RefRenderer>, Receiver<Vec<f32>>) {
    let (tx, rx) = channel();
    let client = Box::new(MyClient{ tx });
    let mut dispatch: Dispatch<RefRenderer> = Dispatch::new();
    dispatch.register_client(client);
    (dispatch, rx)
}

fn create_multby2() -> EffectDesc {
    let mult_hnd = NodeHandle::new_node(DagHandle::toplevel(), 1);
    let mult_data = adjlist::NodeData::Effect(
        EffectId::new("Multiply".into(), None, vec![Url::parse("primitive:///Multiply").unwrap()])
    );
    let const_hnd = NodeHandle::new_node(DagHandle::toplevel(), 2);
    let const_data = adjlist::NodeData::Effect(
        EffectId::new("Constant".into(), None, vec![Url::parse("primitive:///F32Constant").unwrap()])
    );

    let nodes = [(mult_hnd, mult_data), (const_hnd, const_data)];

    // input data sent to multiply (A)
    let edge_in = Edge::new_from_null(mult_hnd, EdgeWeight::new(0, 0));
    // multiply out sent to effect out
    let edge_out = Edge::new_to_null(mult_hnd, EdgeWeight::new(0, 0));
    // const data sent to multiply (B)
    let edge_const = Edge::new(const_hnd, mult_hnd, EdgeWeight::new(pack_f32(5.0f32), 1)).unwrap();

    let edges = [edge_in, edge_out, edge_const];

    let list = AdjList {
        nodes: nodes.iter().cloned().collect(),
        edges: edges.iter().cloned().collect(),
    };
    let meta = EffectMeta::new("MulBy2".into(), None);
    EffectDesc::new(meta, list)
}

#[test]
fn load_multby2() {
    let (mut dispatch, rx) = test_setup();
    let dir = TempDir::new("libfriendship").unwrap();
    let mulby2_desc = create_multby2();

    // Add the temp dir as a search dir
    dispatch.dispatch(
        OscResMan::AddDir((), (dir.path().to_str().unwrap().to_string(),)).into()
    ).unwrap();

    // Write the effect definition to file
    let mulby2_path = dir.path().join("mulby2.fnd");
    let mulby2_file = File::create(mulby2_path.clone()).unwrap();
    serde_json::to_writer(mulby2_file, &mulby2_desc).unwrap();

    // Determine the hash of our file
    let mut mulby2_file = File::open(mulby2_path).unwrap();
    let hash_result = digest_reader::<Sha256>(&mut mulby2_file).unwrap();
    let mut sha: [u8; 32] = Default::default();
    sha.copy_from_slice(hash_result.as_slice());

    // Create the MulBy2 node (id=1)
    let mul_hnd = NodeHandle::new_node(DagHandle::toplevel(), 1);
    dispatch.dispatch(OscRouteGraph::AddNode((), (mul_hnd, adjlist::NodeData::Effect(
        EffectId::new("MulBy2".into(), Some(sha), None)
    ))).into()).unwrap();
    // Connect MulBy2 output to master output.
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new_to_null(mul_hnd, EdgeWeight::new(0, 0)),)).into()).unwrap();

    // Create Constant node (id=2)
    let const_hnd = NodeHandle::new_node(DagHandle::toplevel(), 2);
    dispatch.dispatch(OscRouteGraph::AddNode((), (const_hnd, adjlist::NodeData::Effect(
        EffectId::new("Constant".into(), None, vec![Url::parse("primitive:///F32Constant").unwrap()])
    ))).into()).unwrap();
    // Route constant output to mul input
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new(const_hnd, mul_hnd, EdgeWeight::new(pack_f32(0.5f32), 0)).unwrap(),)).into()).unwrap();

    // Read some data from ch=0.
    // This should be 0.5*5 = [2.5, 2.5, 2.5, 2.5]
    dispatch.dispatch(
        OscRenderer::RenderRange((), (0, 4, 1))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, vec![2.5f32, 2.5f32, 2.5f32, 2.5f32]);
}

<commit_msg>Fix so tests can build (and pass)<commit_after>//! Test loading of effect definitions from the disk.

extern crate libfriendship;
extern crate digest;
extern crate serde_json;
extern crate sha2;
extern crate tempdir;
extern crate url;

use std::fs::File;
use std::sync::mpsc::{channel, Receiver, Sender};

use digest::digest_reader;
use sha2::Sha256;
use tempdir::TempDir;
use url::Url;

use libfriendship::{Dispatch, Client};
use libfriendship::dispatch::{OscRouteGraph, OscRenderer, OscResMan};
use libfriendship::render::RefRenderer;
use libfriendship::routing::{adjlist, NodeHandle, DagHandle, Edge, EdgeWeight, EffectId, EffectDesc, EffectMeta, EffectInput, EffectOutput};
use libfriendship::routing::AdjList;
use libfriendship::util::pack_f32;

struct MyClient {
    /// Where to send the rendered audio.
    tx: Sender<Vec<f32>>,
}
impl Client for MyClient {
    fn audio_rendered(&mut self, buffer: &[f32], _idx: u64, _num_slots: u32) {
        self.tx.send(buffer.iter().cloned().collect()).unwrap();
    }
}

fn test_setup() -> (Dispatch<RefRenderer>, Receiver<Vec<f32>>) {
    let (tx, rx) = channel();
    let client = Box::new(MyClient{ tx });
    let mut dispatch: Dispatch<RefRenderer> = Dispatch::new();
    dispatch.register_client(client);
    (dispatch, rx)
}

fn create_multby2() -> EffectDesc {
    let mult_hnd = NodeHandle::new_node(DagHandle::toplevel(), 1);
    let mult_data = adjlist::NodeData::Effect(
        EffectId::new("Multiply".into(), None, vec![Url::parse("primitive:///Multiply").unwrap()])
    );
    let const_hnd = NodeHandle::new_node(DagHandle::toplevel(), 2);
    let const_data = adjlist::NodeData::Effect(
        EffectId::new("Constant".into(), None, vec![Url::parse("primitive:///F32Constant").unwrap()])
    );

    let nodes = [(mult_hnd, mult_data), (const_hnd, const_data)];

    // input data sent to multiply (A)
    let edge_in = Edge::new_from_null(mult_hnd, EdgeWeight::new(0, 0));
    // multiply out sent to effect out
    let edge_out = Edge::new_to_null(mult_hnd, EdgeWeight::new(0, 0));
    // const data sent to multiply (B)
    let edge_const = Edge::new(const_hnd, mult_hnd, EdgeWeight::new(pack_f32(5.0f32), 1)).unwrap();

    let edges = [edge_in, edge_out, edge_const];

    let list = AdjList {
        nodes: nodes.iter().cloned().collect(),
        edges: edges.iter().cloned().collect(),
    };
    let meta = EffectMeta::new("MulBy2".into(), None,
        [(0, EffectInput::new("source".into(), 0))].iter().cloned().collect(),
        [(0, EffectOutput::new("result".into(), 0))].iter().cloned().collect(),
    );
    EffectDesc::new(meta, list)
}

#[test]
fn load_multby2() {
    let (mut dispatch, rx) = test_setup();
    let dir = TempDir::new("libfriendship").unwrap();
    let mulby2_desc = create_multby2();

    // Add the temp dir as a search dir
    dispatch.dispatch(
        OscResMan::AddDir((), (dir.path().to_str().unwrap().to_string(),)).into()
    ).unwrap();

    // Write the effect definition to file
    let mulby2_path = dir.path().join("mulby2.fnd");
    let mulby2_file = File::create(mulby2_path.clone()).unwrap();
    serde_json::to_writer(mulby2_file, &mulby2_desc).unwrap();

    // Determine the hash of our file
    let mut mulby2_file = File::open(mulby2_path).unwrap();
    let hash_result = digest_reader::<Sha256>(&mut mulby2_file).unwrap();
    let mut sha: [u8; 32] = Default::default();
    sha.copy_from_slice(hash_result.as_slice());

    // Create the MulBy2 node (id=1)
    let mul_hnd = NodeHandle::new_node(DagHandle::toplevel(), 1);
    dispatch.dispatch(OscRouteGraph::AddNode((), (mul_hnd, adjlist::NodeData::Effect(
        EffectId::new("MulBy2".into(), Some(sha), None)
    ))).into()).unwrap();
    // Connect MulBy2 output to master output.
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new_to_null(mul_hnd, EdgeWeight::new(0, 0)),)).into()).unwrap();

    // Create Constant node (id=2)
    let const_hnd = NodeHandle::new_node(DagHandle::toplevel(), 2);
    dispatch.dispatch(OscRouteGraph::AddNode((), (const_hnd, adjlist::NodeData::Effect(
        EffectId::new("Constant".into(), None, vec![Url::parse("primitive:///F32Constant").unwrap()])
    ))).into()).unwrap();
    // Route constant output to mul input
    dispatch.dispatch(OscRouteGraph::AddEdge((), (Edge::new(const_hnd, mul_hnd, EdgeWeight::new(pack_f32(0.5f32), 0)).unwrap(),)).into()).unwrap();

    // Read some data from ch=0.
    // This should be 0.5*5 = [2.5, 2.5, 2.5, 2.5]
    dispatch.dispatch(
        OscRenderer::RenderRange((), (0, 4, 1))
    .into()).unwrap();
    let rendered = rx.recv().unwrap();
    assert_eq!(rendered, vec![2.5f32, 2.5f32, 2.5f32, 2.5f32]);
}

<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#[no_mangle]
extern "C" {
    fn __wasilibc_initialize_environ();
}

#[no_mangle]
pub extern "C" fn run() {
    unsafe {
        __wasilibc_initialize_environ();
    }
    for (key, value) in std::env::vars() {
        println!("{}: {}\n", key, value);
    }
}
<commit_msg>test: remove no_mangle from extern "c" (#180)<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern "C" {
    fn __wasilibc_initialize_environ();
}

#[no_mangle]
pub extern "C" fn run() {
    unsafe {
        __wasilibc_initialize_environ();
    }
    for (key, value) in std::env::vars() {
        println!("{}: {}\n", key, value);
    }
}
<|endoftext|>


<commit_before>use common::*;
use io::MBuf;
use std::ptr;
use std::marker::PhantomData;
use std::slice;
use headers::{EndOffset, NullHeader};
// Some low level functions.
#[link(name = "zcsi")]
extern "C" {
    fn mbuf_alloc() -> *mut MBuf;
    fn mbuf_free(buf: *mut MBuf);
    fn mbuf_alloc_bulk(array: *mut *mut MBuf, len: u16, cnt: i32) -> i32;
}

/// A packet is a safe wrapper around mbufs, that can be allocated and manipulated.
/// We associate a header type with a packet to allow safe insertion of headers.
#[cfg(not(feature = "packet_offset"))]
pub struct Packet<T: EndOffset> {
    mbuf: *mut MBuf,
    _phantom_t: PhantomData<T>,
    header: *mut T,
    offset: usize
}

#[cfg(feature = "packet_offset")]
pub struct Packet<T: EndOffset> {
    mbuf: *mut MBuf,
    _phantom_t: PhantomData<T>,
}

fn reference_mbuf(mbuf: *mut MBuf) {
    unsafe { (*mbuf).reference() };
}

#[cfg(feature = "packet_offset")]
const HEADER_SLOT: usize = 0;
#[cfg(feature = "packet_offset")]
const OFFSET_SLOT: usize = 1;

#[inline]
#[cfg(feature = "packet_offset")]
fn create_packet<T: EndOffset>(mbuf: *mut MBuf, hdr: *mut T, offset: usize) -> Packet<T> {
    let mut pkt = Packet::<T> {
        mbuf: mbuf,
        _phantom_t: PhantomData,
    };
    pkt.update_ptrs(hdr as *mut u8, offset);
    pkt
}

#[inline]
#[cfg(not(feature = "packet_offset"))]
fn create_packet<T: EndOffset>(mbuf: *mut MBuf, hdr: *mut T, offset: usize) -> Packet<T> {
    Packet::<T> {
        mbuf: mbuf,
        _phantom_t: PhantomData,
        offset: offset,
        header: hdr,
    }
}

#[inline]
pub fn packet_from_mbuf<T:EndOffset>(mbuf: *mut MBuf, offset: usize) -> Packet<T> {
        // Need to up the refcnt, so that things don't drop.
        reference_mbuf(mbuf);
        packet_from_mbuf_no_increment(mbuf, offset)
}

#[inline]
pub fn packet_from_mbuf_no_increment<T:EndOffset>(mbuf: *mut MBuf, offset: usize) -> Packet<T> {
    unsafe {
        // Compute the real offset
        let header =  (*mbuf).data_address(offset) as *mut T;
        create_packet(mbuf, header, offset)
    }
}

#[inline]
pub unsafe fn packet_from_mbuf_no_free<T:EndOffset>(mbuf: *mut MBuf, offset: usize) -> Packet<T> {
    packet_from_mbuf_no_increment(mbuf, offset)
}

/// Allocate a new packet.
pub fn new_packet() -> Option<Packet<NullHeader>> {
    unsafe {
        // This sets refcnt = 1
        let mbuf = mbuf_alloc();
        if mbuf.is_null() {
            None
        } else {
            Some(packet_from_mbuf_no_increment(mbuf, 0))
        }
    }
}

pub fn new_packet_array(count: usize) -> Vec<Packet<NullHeader>> {
    let mut array = Vec::with_capacity(count);
    unsafe {
        let alloc_ret = mbuf_alloc_bulk(array.as_mut_ptr(), 0, count as i32);
        if alloc_ret == 0 {
            array.set_len(count);
        }
    }
    array.iter().map(|m| packet_from_mbuf_no_increment(m.clone(), 0)).collect()
}

pub const METADATA_SLOTS : u16 = 8;

impl<T: EndOffset> Packet<T> {
    #[inline]
    pub fn free_packet(self) {
        if !self.mbuf.is_null() {
            unsafe { mbuf_free(self.mbuf) };
        }
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn update_ptrs(&mut self, header: *mut u8, offset: usize) {
        if false {
            println!("packet {:x} update_ptrs header {:x} offset {:x}", self.mbuf as usize, header as usize, offset as
                     usize);
        }
        MBuf::write_metadata_slot(self.mbuf, HEADER_SLOT, header as usize);
        MBuf::write_metadata_slot(self.mbuf, OFFSET_SLOT, offset as usize);
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn header(&self) -> *mut T {
        MBuf::read_metadata_slot(self.mbuf, HEADER_SLOT) as *mut T
    }

    #[inline]
    #[cfg(not(feature="packet_offset"))]
    fn header(&self) -> *mut T {
        //MBuf::read_metadata_slot(self.mbuf, HEADER_SLOT) as *mut T
        self.header
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn header_u8(&self) -> *mut u8 {
        MBuf::read_metadata_slot(self.mbuf, HEADER_SLOT) as *mut u8
    }

    #[inline]
    #[cfg(not(feature="packet_offset"))]
    fn header_u8(&self) -> *mut u8 {
        self.header as *mut u8
    }

    #[inline]
    fn payload(&self) -> *mut u8 {
        unsafe {
            let payload_offset = self.payload_offset();
            self.header_u8().offset(payload_offset as isize)
        }
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn offset(&self) -> usize {
        MBuf::read_metadata_slot(self.mbuf, OFFSET_SLOT)
    }

    #[inline]
    #[cfg(not(feature="packet_offset"))]
    fn offset(&self) -> usize {
        self.offset
    }

    #[inline]
    fn payload_offset(&self) -> usize {
        unsafe { (*self.header()).offset() }
    }

    #[inline]
    fn data_base(&self) -> *mut u8 {
        unsafe { (*self.mbuf).data_address(0) }
    }

    #[inline]
    fn data_len(&self) -> usize {
        unsafe { (*self.mbuf).data_len() }
    }

    #[inline]
    fn payload_size(&self) -> usize {
        self.data_len() - self.offset()
    }

    #[inline]
    pub fn get_header(&self) -> &T {
        unsafe { &(*(self.header())) }
    }

    #[inline]
    pub fn get_mut_header(&mut self) -> &mut T {
        unsafe { &mut (*(self.header())) }
    }

    #[inline]
    pub fn push_header<T2: EndOffset<PreviousHeader = T>>(mut self, header: &T2) -> Option<Packet<T2>> {
        unsafe {
            let len = self.data_len();
            let size = header.offset();
            let added = (*self.mbuf).add_data_end(size);

            let header = self.payload();

            let hdr = header as *mut T2;
            let offset = self.offset() + self.payload_offset();
            if added >= size {
                let dst = if len != offset {
                    // Need to move down the rest of the data down.
                    let final_dst = self.payload();
                    let move_loc = final_dst.offset(size as isize);
                    let to_move = len - offset;
                    ptr::copy_nonoverlapping(final_dst, move_loc, to_move);
                    final_dst as *mut T2
                } else {
                    self.payload() as *mut T2
                };
                ptr::copy_nonoverlapping(hdr, dst, 1);
                Some(create_packet(self.get_mbuf(), hdr, offset))
            } else {
                None
            }
        }
    }

    #[inline]
    pub fn add_headroom(&mut self, size: usize) -> Result<()>{
        unsafe {
            let added = (*self.mbuf).add_data_end(size);
            if added >= size {
                let src = self.payload();
                let dst = src.offset(size as isize);
                ptr::copy_nonoverlapping(src, dst, size);
                Ok(())
            } else {
                Err(ZCSIError::FailedAllocation)
            }
        }
    }

    #[inline]
    pub fn write_header<T2: EndOffset + Sized>(&mut self, header: &T2, offset: usize) -> Result<()>{
        if offset >self.payload_size() {
            Err(ZCSIError::BadOffset)
        } else {
            unsafe {
                let dst = self.payload().offset(offset as isize);
                ptr::copy_nonoverlapping(header, dst as *mut T2, 1);
            }
            Ok(())
        }
    }

    #[inline]
    pub fn parse_header<T2: EndOffset<PreviousHeader = T>>(mut self) -> Packet<T2> {
        unsafe {
            let hdr = self.payload() as *mut T2;
            let offset = self.offset() + self.payload_offset();
            create_packet(self.get_mbuf(), hdr, offset)
        }
    }

    #[inline]
    pub fn deparse_header(mut self, offset: usize) -> Packet<T::PreviousHeader> {
        let offset = offset as isize;
        unsafe {
            let header = self.header_u8().offset(-offset) as *mut T::PreviousHeader;
            let new_offset = self.offset() - offset as usize;
            create_packet(self.get_mbuf(), header, new_offset)
        }
    }

    #[inline]
    pub fn reset(mut self) -> Packet<NullHeader> {
        unsafe {
            let mbuf = self.get_mbuf();
            let header = self.data_base() as *mut NullHeader;
            create_packet(mbuf, header, 0)
        }
    }

    #[inline]
    pub fn get_mut_payload(&mut self) -> &mut [u8] {
        unsafe {
            let len = self.payload_size();
            let ptr = self.payload();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    #[inline]
    pub fn get_payload(&self) -> &[u8] {
        unsafe {
            let len = self.payload_size();
            slice::from_raw_parts(self.payload(), len)
        }
    }

    #[inline]
    pub fn increase_payload_size(&mut self, increase_by: usize) -> usize {
        unsafe { (*self.mbuf).add_data_end(increase_by) }
    }

    #[inline]
    pub fn trim_payload_size(&mut self, trim_by: usize) -> usize {
        unsafe { (*self.mbuf).remove_data_end(trim_by) }
    }

    #[inline]
    pub fn copy_payload(&mut self, other: &Self) -> usize {
        let copy_len = other.payload_size();
        let dst = self.payload();
        let src = other.payload();

        let payload_size = self.payload_size();

        let should_copy = if payload_size < copy_len {
            let increment = copy_len - payload_size;
            self.increase_payload_size(increment)
        } else {
            copy_len
        };

        unsafe {
            ptr::copy_nonoverlapping(src, dst, should_copy);
            should_copy
        }
    }

    #[inline]
    pub fn refcnt(&self) -> u16 {
        unsafe { (*self.mbuf).refcnt() }
    }

    /// Get the mbuf reference by this packet.
    ///
    /// # Safety
    /// The reference held by this Packet is nulled out as a result of this code. The callee is responsible for
    /// appropriately freeing this mbuf from here-on out.
    #[inline]
    pub unsafe fn get_mbuf(&mut self) -> *mut MBuf {
        let mbuf = self.mbuf;
        self.mbuf = ptr::null_mut();
        mbuf
    }
}
<commit_msg>Add some convenience methods for packets<commit_after>use common::*;
use io::MBuf;
use std::ptr;
use std::marker::PhantomData;
use std::slice;
use headers::{EndOffset, NullHeader};
// Some low level functions.
#[link(name = "zcsi")]
extern "C" {
    fn mbuf_alloc() -> *mut MBuf;
    fn mbuf_free(buf: *mut MBuf);
    fn mbuf_alloc_bulk(array: *mut *mut MBuf, len: u16, cnt: i32) -> i32;
}

/// A packet is a safe wrapper around mbufs, that can be allocated and manipulated.
/// We associate a header type with a packet to allow safe insertion of headers.
#[cfg(not(feature = "packet_offset"))]
pub struct Packet<T: EndOffset> {
    mbuf: *mut MBuf,
    _phantom_t: PhantomData<T>,
    header: *mut T,
    offset: usize
}

#[cfg(feature = "packet_offset")]
pub struct Packet<T: EndOffset> {
    mbuf: *mut MBuf,
    _phantom_t: PhantomData<T>,
}

fn reference_mbuf(mbuf: *mut MBuf) {
    unsafe { (*mbuf).reference() };
}

#[cfg(feature = "packet_offset")]
const HEADER_SLOT: usize = 0;
#[cfg(feature = "packet_offset")]
const OFFSET_SLOT: usize = 1;

#[inline]
#[cfg(feature = "packet_offset")]
fn create_packet<T: EndOffset>(mbuf: *mut MBuf, hdr: *mut T, offset: usize) -> Packet<T> {
    let mut pkt = Packet::<T> {
        mbuf: mbuf,
        _phantom_t: PhantomData,
    };
    pkt.update_ptrs(hdr as *mut u8, offset);
    pkt
}

#[inline]
#[cfg(not(feature = "packet_offset"))]
fn create_packet<T: EndOffset>(mbuf: *mut MBuf, hdr: *mut T, offset: usize) -> Packet<T> {
    Packet::<T> {
        mbuf: mbuf,
        _phantom_t: PhantomData,
        offset: offset,
        header: hdr,
    }
}

#[inline]
pub fn packet_from_mbuf<T:EndOffset>(mbuf: *mut MBuf, offset: usize) -> Packet<T> {
        // Need to up the refcnt, so that things don't drop.
        reference_mbuf(mbuf);
        packet_from_mbuf_no_increment(mbuf, offset)
}

#[inline]
pub fn packet_from_mbuf_no_increment<T:EndOffset>(mbuf: *mut MBuf, offset: usize) -> Packet<T> {
    unsafe {
        // Compute the real offset
        let header =  (*mbuf).data_address(offset) as *mut T;
        create_packet(mbuf, header, offset)
    }
}

#[inline]
pub unsafe fn packet_from_mbuf_no_free<T:EndOffset>(mbuf: *mut MBuf, offset: usize) -> Packet<T> {
    packet_from_mbuf_no_increment(mbuf, offset)
}

/// Allocate a new packet.
pub fn new_packet() -> Option<Packet<NullHeader>> {
    unsafe {
        // This sets refcnt = 1
        let mbuf = mbuf_alloc();
        if mbuf.is_null() {
            None
        } else {
            Some(packet_from_mbuf_no_increment(mbuf, 0))
        }
    }
}

pub fn new_packet_array(count: usize) -> Vec<Packet<NullHeader>> {
    let mut array = Vec::with_capacity(count);
    unsafe {
        let alloc_ret = mbuf_alloc_bulk(array.as_mut_ptr(), 0, count as i32);
        if alloc_ret == 0 {
            array.set_len(count);
        }
    }
    array.iter().map(|m| packet_from_mbuf_no_increment(m.clone(), 0)).collect()
}

pub const METADATA_SLOTS : u16 = 8;

impl<T: EndOffset> Packet<T> {
    #[inline]
    pub fn free_packet(self) {
        if !self.mbuf.is_null() {
            unsafe { mbuf_free(self.mbuf) };
        }
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn update_ptrs(&mut self, header: *mut u8, offset: usize) {
        if false {
            println!("packet {:x} update_ptrs header {:x} offset {:x}", self.mbuf as usize, header as usize, offset as
                     usize);
        }
        MBuf::write_metadata_slot(self.mbuf, HEADER_SLOT, header as usize);
        MBuf::write_metadata_slot(self.mbuf, OFFSET_SLOT, offset as usize);
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn header(&self) -> *mut T {
        MBuf::read_metadata_slot(self.mbuf, HEADER_SLOT) as *mut T
    }

    #[inline]
    #[cfg(not(feature="packet_offset"))]
    fn header(&self) -> *mut T {
        //MBuf::read_metadata_slot(self.mbuf, HEADER_SLOT) as *mut T
        self.header
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn header_u8(&self) -> *mut u8 {
        MBuf::read_metadata_slot(self.mbuf, HEADER_SLOT) as *mut u8
    }

    #[inline]
    #[cfg(not(feature="packet_offset"))]
    fn header_u8(&self) -> *mut u8 {
        self.header as *mut u8
    }

    #[inline]
    fn payload(&self) -> *mut u8 {
        unsafe {
            let payload_offset = self.payload_offset();
            self.header_u8().offset(payload_offset as isize)
        }
    }

    #[inline]
    #[cfg(feature="packet_offset")]
    fn offset(&self) -> usize {
        MBuf::read_metadata_slot(self.mbuf, OFFSET_SLOT)
    }

    #[inline]
    #[cfg(not(feature="packet_offset"))]
    fn offset(&self) -> usize {
        self.offset
    }

    #[inline]
    fn payload_offset(&self) -> usize {
        unsafe { (*self.header()).offset() }
    }

    #[inline]
    fn data_base(&self) -> *mut u8 {
        unsafe { (*self.mbuf).data_address(0) }
    }

    #[inline]
    fn data_len(&self) -> usize {
        unsafe { (*self.mbuf).data_len() }
    }

    #[inline]
    fn payload_size(&self) -> usize {
        self.data_len() - self.offset()
    }

    #[inline]
    pub fn get_header(&self) -> &T {
        unsafe { &(*(self.header())) }
    }

    #[inline]
    pub fn get_mut_header(&mut self) -> &mut T {
        unsafe { &mut (*(self.header())) }
    }

    /// When constructing a packet, take a packet as input and add a header.
    #[inline]
    pub fn push_header<T2: EndOffset<PreviousHeader = T>>(mut self, header: &T2) -> Option<Packet<T2>> {
        unsafe {
            let len = self.data_len();
            let size = header.offset();
            let added = (*self.mbuf).add_data_end(size);

            let header = self.payload();

            let hdr = header as *mut T2;
            let offset = self.offset() + self.payload_offset();
            if added >= size {
                let dst = if len != offset {
                    // Need to move down the rest of the data down.
                    let final_dst = self.payload();
                    let move_loc = final_dst.offset(size as isize);
                    let to_move = len - offset;
                    ptr::copy_nonoverlapping(final_dst, move_loc, to_move);
                    final_dst as *mut T2
                } else {
                    self.payload() as *mut T2
                };
                ptr::copy_nonoverlapping(hdr, dst, 1);
                Some(create_packet(self.get_mbuf(), hdr, offset))
            } else {
                None
            }
        }
    }

    /// Remove data at the top of the payload, useful when removing headers.
    #[inline]
    pub fn remove_from_payload_head(&mut self, size: usize) -> Result<()> {
        unsafe {
            let src = self.data_base();
            let dst = src.offset(size as isize);
            ptr::copy_nonoverlapping(src, dst, size);
            (*self.mbuf).remove_data_beginning(size);
            Ok(())
        }
    }

    /// Add data to the head of the payload.
    #[inline]
    pub fn add_to_payload_head(&mut self, size: usize) -> Result<()>{
        unsafe {
            let added = (*self.mbuf).add_data_end(size);
            if added >= size {
                let src = self.payload();
                let dst = src.offset(size as isize);
                ptr::copy_nonoverlapping(src, dst, size);
                Ok(())
            } else {
                Err(ZCSIError::FailedAllocation)
            }
        }
    }

    #[inline]
    pub fn remove_from_payload_tail(&mut self, size: usize) -> Result<()> {
        unsafe {
            (*self.mbuf).remove_data_end(size);
            Ok(())
        }
    }

    #[inline]
    pub fn add_to_payload_tail(&mut self, size: usize) -> Result<()>{
        unsafe {
            let added = (*self.mbuf).add_data_end(size);
            if added >= size {
                Ok(())
            } else {
                Err(ZCSIError::FailedAllocation)
            }
        }
    }

    #[inline]
    pub fn write_header<T2: EndOffset + Sized>(&mut self, header: &T2, offset: usize) -> Result<()>{
        if offset >self.payload_size() {
            Err(ZCSIError::BadOffset)
        } else {
            unsafe {
                let dst = self.payload().offset(offset as isize);
                ptr::copy_nonoverlapping(header, dst as *mut T2, 1);
            }
            Ok(())
        }
    }

    #[inline]
    pub fn parse_header<T2: EndOffset<PreviousHeader = T>>(mut self) -> Packet<T2> {
        unsafe {
            let hdr = self.payload() as *mut T2;
            let offset = self.offset() + self.payload_offset();
            create_packet(self.get_mbuf(), hdr, offset)
        }
    }

    #[inline]
    pub fn replace_header(&mut self, hdr: &T) {
        unsafe {
            ptr::copy_nonoverlapping(hdr, self.header(), 1);
        }
    }

    #[inline]
    pub fn deparse_header(mut self, offset: usize) -> Packet<T::PreviousHeader> {
        let offset = offset as isize;
        unsafe {
            let header = self.header_u8().offset(-offset) as *mut T::PreviousHeader;
            let new_offset = self.offset() - offset as usize;
            create_packet(self.get_mbuf(), header, new_offset)
        }
    }

    #[inline]
    pub fn reset(mut self) -> Packet<NullHeader> {
        unsafe {
            let mbuf = self.get_mbuf();
            let header = self.data_base() as *mut NullHeader;
            create_packet(mbuf, header, 0)
        }
    }

    #[inline]
    pub fn get_mut_payload(&mut self) -> &mut [u8] {
        unsafe {
            let len = self.payload_size();
            let ptr = self.payload();
            slice::from_raw_parts_mut(ptr, len)
        }
    }

    #[inline]
    pub fn get_payload(&self) -> &[u8] {
        unsafe {
            let len = self.payload_size();
            slice::from_raw_parts(self.payload(), len)
        }
    }

    #[inline]
    pub fn increase_payload_size(&mut self, increase_by: usize) -> usize {
        unsafe { (*self.mbuf).add_data_end(increase_by) }
    }

    #[inline]
    pub fn trim_payload_size(&mut self, trim_by: usize) -> usize {
        unsafe { (*self.mbuf).remove_data_end(trim_by) }
    }

    #[inline]
    pub fn copy_payload(&mut self, other: &Self) -> usize {
        let copy_len = other.payload_size();
        let dst = self.payload();
        let src = other.payload();

        let payload_size = self.payload_size();

        let should_copy = if payload_size < copy_len {
            let increment = copy_len - payload_size;
            self.increase_payload_size(increment)
        } else {
            copy_len
        };

        unsafe {
            ptr::copy_nonoverlapping(src, dst, should_copy);
            should_copy
        }
    }

    #[inline]
    pub fn refcnt(&self) -> u16 {
        unsafe { (*self.mbuf).refcnt() }
    }

    /// Get the mbuf reference by this packet.
    ///
    /// # Safety
    /// The reference held by this Packet is nulled out as a result of this code. The callee is responsible for
    /// appropriately freeing this mbuf from here-on out.
    #[inline]
    pub unsafe fn get_mbuf(&mut self) -> *mut MBuf {
        let mbuf = self.mbuf;
        self.mbuf = ptr::null_mut();
        mbuf
    }
}
<|endoftext|>


<commit_before>use weak_gradient::*;
use polynomial;
use polynomial::{Polynomial, PolyOwning, PolyBorrowingMons, approx_equiv};
use monomial::{Mon2d, MaxMonDeg};
use mesh::{OShape};
use rectangle_mesh::{RectMesh, MeshCoord};

use common::*;
use std::vec;

// Tests below use the fact that if a polynomial p is in P_k(e) for some finite element e, where the
// weak gradient approximation space is [P_{k-1}(e)]^d, then wgrad(p) = grad(p) on the interior of e.
// Thus we can test weak gradient calculations on such polynomials and verify this equality. However,
// the weak gradient module only computes weak gradients of shape functions defined piecewise to be
// monomials on a single supporting face and 0 elsewhere. So to compute weak gradients of a polynomial
// across an entire finite element, we consider the polynomial as the sum of face-relative shape functions,
// and using the linearity of the weak gradient, we then compute the original polynomial's weak gradient
// as the sum of the weak gradients of these shape functions.

#[test]
fn test_wgrad_xy() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let mut wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(3), rmesh);

  let one = Mon2d { exps: [Deg(0), Deg(0)] };
  let x = Mon2d { exps: [Deg(1), Deg(0)] };
  let y = Mon2d { exps: [Deg(0), Deg(1)] };

  let (int_mon_wgrads, side_mon_wgrads) =
    wgrad_solver.wgrads_on_oshape([x*y],   // interior
                                  [&[one], // left side (ignored because xy is 0 here)
                                  &[y],   // right
                                  &[one], // bottom    (ignored ")
                                  &[x]],  // top
                                  OShape(0),
                                  rmesh);

  let xy_on_int_wgrad = &int_mon_wgrads[0];
  let y_on_right_side_wgrad = &side_mon_wgrads[1][0];
  let x_on_top_side_wgrad = &side_mon_wgrads[3][0];

  let wgrad = &lcomb_wgrads([(1., xy_on_int_wgrad), (1., y_on_right_side_wgrad), (1., x_on_top_side_wgrad)]);

  let wgrad_comp0 = PolyOwning::new(wgrad.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons.clone());
  let wgrad_comp1 = PolyOwning::new(wgrad.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons.clone());

  assert!(approx_equiv(&wgrad_comp0, &PolyOwning::new(~[1.],~[y]), 1e-9));
  assert!(approx_equiv(&wgrad_comp1, &PolyOwning::new(~[1.],~[x]), 1e-9));
}

#[test]
fn test_wgrad_x2y() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let mut wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(3), rmesh);

  let one = Mon2d { exps: [Deg(0), Deg(0)] };
  let x = Mon2d { exps: [Deg(1), Deg(0)] };
  let y = Mon2d { exps: [Deg(0), Deg(1)] };

  let (int_mon_wgrads, side_mon_wgrads) =
    wgrad_solver.wgrads_on_oshape([x*x*y],  // interior
                                  [&[one],  // left side (ignored because xy is 0 here)
                                  &[y],     // right
                                  &[one],   // bottom    (ignored ")
                                  &[x*x]],  // top
                                  OShape(0),
                                  rmesh);

  let x2y_on_int_wgrad = &int_mon_wgrads[0];
  let y_on_right_side_wgrad = &side_mon_wgrads[1][0];
  let x2_on_top_side_wgrad = &side_mon_wgrads[3][0];

  let wgrad = &lcomb_wgrads([(1., x2y_on_int_wgrad), (1., y_on_right_side_wgrad), (1., x2_on_top_side_wgrad)]);

  let wgrad_comp0 = PolyOwning::new(wgrad.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons.clone());
  let wgrad_comp1 = PolyOwning::new(wgrad.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons.clone());

  assert!(approx_equiv(&wgrad_comp0, &PolyOwning::new(~[2.],~[x*y]), 1e-9));
  assert!(approx_equiv(&wgrad_comp1, &PolyOwning::new(~[1.],~[x*x]), 1e-9));
}

#[test]
fn test_wgrad_dot() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(2), rmesh);

  let wgrad1 = WeakGrad { comp_mon_coefs: ~[~[-2.5, 3., 0., 15., -6., -15.], ~[-2.5, 15., -15., 3., -6., 0.]] };
  let wgrad2 = WeakGrad { comp_mon_coefs: ~[~[2.5, -2., -0., -15., 6., 15.], ~[0., 0., 0., 0., 0., 0.]] };

  let mut wgrad_ops = wgrad_solver.weak_grad_ops();

  let fast_dot_prod = wgrad_ops.dot(&wgrad1, &wgrad2);

  // Compiler prevents second call of dot() which would have silently overwritten the existing wgrad's shared coefficients buffer. Beautiful!
  // let wgrad_x_top_dot_wgrad_y_right = wgrad_ops.dot(&wgrad1, &wgrad1);
  //   =>  error: cannot borrow `*wgrad_ops` as mutable more than once at a time

  let p1_0 = PolyBorrowingMons::new(wgrad1.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons);
  let p1_1 = PolyBorrowingMons::new(wgrad1.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons);
  let p2_0 = PolyBorrowingMons::new(wgrad2.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons);
  let p2_1 = PolyBorrowingMons::new(wgrad2.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons);
  let polys_dot_prod = PolyOwning::from_polys_lcomb([(1., &polynomial::mul(&p1_0, &p2_0)), (1., &polynomial::mul(&p1_1, &p2_1))]);

  println!("fast dot product:  {}", fast_dot_prod.to_str());
  println!("polys dot product: {}", polys_dot_prod.to_str());

  let canon_fast_dot_prod = fast_dot_prod.canonical_form();
  assert_eq!(&canon_fast_dot_prod.coefs, &polys_dot_prod.coefs);
  assert_eq!(&canon_fast_dot_prod.mons, &polys_dot_prod.mons);
}

fn lcomb_wgrads(terms: &[(R,&WeakGrad)]) -> WeakGrad {
  if terms.len() == 0 { fail!("lcomb_wgrads: At least one weak gradient is required.") }
  let (space_dims, num_comp_mons) = match terms[0] { (_, wgrad) => (wgrad.comp_mon_coefs.len(), wgrad.comp_mon_coefs[0].len()) };
  WeakGrad {
    comp_mon_coefs:
      vec::from_fn(space_dims, |d|
        vec::from_fn(num_comp_mons, |mon_num|
          terms.iter().fold(0 as R, |sum, &(c, wgrad)| sum + c * wgrad.comp_mon_coefs[d][mon_num])))
  }
}

<commit_msg>Add weak grad tests of individual monomials worked manually.<commit_after>use weak_gradient::*;
use polynomial;
use polynomial::{Polynomial, PolyOwning, PolyBorrowingMons, approx_equiv};
use monomial::{Mon2d, MaxMonDeg};
use mesh::{OShape};
use rectangle_mesh::{RectMesh, MeshCoord};

use common::*;
use std::vec;

#[test]
fn test_top_x_wgrad() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let mut wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(1), rmesh);

  let x = Mon2d { exps: [Deg(1), Deg(0)] };

  /* Component mon deg lim = 1
   * {q_i}_i = { (1,0), (y,0), (x,0), (0,1), (0,y), (0,x) }
   * v = x on top side, 0 elsewhere
   *
   * Integrals of interior relative vector monomial dot products over the interior of the finite element, which in our case is the unit square.
   * IP = inner products ({q_i}_i) =
   *   int{(1,0).(1,0)}    int{(1,0).(y,0)}  int{(1,0).(x,0)} int{(1,0).(0,1)}, int{(1,0).(0,y)}  int{(1,0).(0,x)}
   *   int{(y,0).(1,0)}    int{(y,0).(y,0)}  int{(y,0).(x,0)} int{(y,0).(0,1)}, int{(y,0).(0,y)}  int{(y,0).(0,x)}
   *   int{(x,0).(1,0)}    int{(x,0).(y,0)}  int{(x,0).(x,0)} int{(x,0).(0,1)}, int{(x,0).(0,y)}  int{(x,0).(0,x)}
   *   int{(0,1).(1,0)}    int{(0,1).(y,0)}  int{(0,1).(x,0)} int{(0,1).(0,1)}, int{(0,1).(0,y)}  int{(0,1).(0,x)}
   *   int{(0,y).(1,0)}    int{(0,y).(y,0)}  int{(0,y).(x,0)} int{(0,y).(0,1)}, int{(0,y).(0,y)}  int{(0,y).(0,x)}
   *   int{(0,x).(1,0)}    int{(0,x).(y,0)}  int{(0,x).(x,0)} int{(0,x).(0,1)}, int{(0,x).(0,y)}  int{(0,x).(0,x)}
   *   =
   *            1                   1/2               1/2              0                 0                 0
   *           1/2                  1/3               1/4              0                 0                 0
   *           1/2                  1/4               1/3              0                 0                 0
   *            0                    0                 0               1                1/2               1/2
   *            0                    0                 0              1/2               1/3               1/4
   *            0                    0                 0              1/2               1/4               1/3
   * [WGRAD_DEF]
   *   (wgrad(v), q)_T = -(v_0, div q)_T + <v_b, q.n>_bnd(T), for all q in [P_r(T)]^d
   *
   * v_0 is 0 because our v is supported on the top side, so the first term of the RHS is 0 for each q.
   * [Beware:
   *   Here, as in the method itself, v is expressed as a *face-relative* monomial on its support face, while q is
   *   naturally expressed as an interior-relative vector monomial. This means that when v is side-supported, care
   *   must be taken to use a common coordinate system when for calculations involving q and v. For rectangle
   *   elements, this doesn't present any difficulty because the two coordinate systems will only differ in a
   *   single dimension (the side's perpendicular axis dimension), and monomials supported on a side face will
   *   be missing the problematic factor variable (otherwise they would be 0 and could not be part of a basis).
   *   For other element shapes conversion of at least one of q and v to a common coordinate system should be done
   *   to obtain correct results.]
   * RHS =
   *   [q=(1,0)]: <x, (1,0).(0,1)>_top(T) = 0
   *   [q=(y,0)]: <x, (y,0).(0,1)>_top(T) = 0
   *   [q=(x,0)]: <x, (x,0).(0,1)>_top(T) = 0
   *   [q=(0,1)]: <x, (0,1).(0,1)>_top(T) = 1/2
   *   [q=(0,y)]: <x, (0,y).(0,1)>_top(T) = 1/2
   *   [q=(0,x)]: <x, (0,x).(0,1)>_top(T) = 1/3
   *
   * Now we solve the system
   *   IP xi = RHS
   * This gives us the our solution as a linear combination of the q_i:
   *   sol = sum_i xi_i q_i
   *   where
   *     xi = [0 0 0 -3/2 3 1]'
   */

  let (int_mon_wgrads, side_mon_wgrads) =
    wgrad_solver.wgrads_on_oshape([x],    // interior
                                  [&[x],  // left side
                                   &[x],  // right
                                   &[x],  // bottom
                                   &[x]], // top
                                  OShape(0),
                                  rmesh);

  let x_on_top_side_wgrad = &side_mon_wgrads[3][0];

  assert_eq!(x_on_top_side_wgrad.comp_mon_coefs[0].as_slice(), &[0., 0., 0.]);
  assert_eq!(x_on_top_side_wgrad.comp_mon_coefs[1].as_slice(), &[-3./2., 3., 1.]);
}

#[test]
fn test_right_y_wgrad() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let mut wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(1), rmesh);

  let y = Mon2d { exps: [Deg(0), Deg(1)] };

  /* System matrix is as above, only the right hand side of the system differs.
   *
   * [WGRAD_DEF]
   *   (wgrad(v), q)_T = -(v_0, div q)_T + <v_b, q.n>_bnd(T), for all q in [P_r(T)]^d
   *
   * RHS =
   *   [q=(1,0)]: <y, (1,0).(1,0)>_top(T) = 1/2
   *   [q=(y,0)]: <y, (y,0).(1,0)>_top(T) = 1/3
   *   [q=(x,0)]: <y, (x,0).(1,0)>_top(T) = 1/2
   *   [q=(0,1)]: <y, (0,1).(1,0)>_top(T) = 0
   *   [q=(0,y)]: <y, (0,y).(1,0)>_top(T) = 0
   *   [q=(0,x)]: <y, (0,x).(1,0)>_top(T) = 0
   *
   * Now we solve the system
   *   IP xi = RHS
   * This gives us the our solution as a linear combination of the q_i:
   *   sol = sum_i xi_i q_i
   *   where
   *     xi = [-3/2 1 3 0 0 0]'
   */

  let (int_mon_wgrads, side_mon_wgrads) =
    wgrad_solver.wgrads_on_oshape([y],    // interior
                                  [&[y],  // left side
                                   &[y],  // right
                                   &[y],  // bottom
                                   &[y]], // top
                                  OShape(0),
                                  rmesh);

  let y_on_right_side_wgrad = &side_mon_wgrads[1][0];

  assert_eq!(y_on_right_side_wgrad.comp_mon_coefs[0].as_slice(), &[-3./2., 1., 3.]);
  assert_eq!(y_on_right_side_wgrad.comp_mon_coefs[1].as_slice(), &[0., 0., 0.]);
}

#[test]
fn test_int_xy_wgrad() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let mut wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(1), rmesh);

  let xy = Mon2d { exps: [Deg(1), Deg(1)] };

  /* System matrix is as above, only the right hand side of the system differs.
   *
   * [WGRAD_DEF]
   *   (wgrad(v), q)_T = -(v_0, div q)_T + <v_b, q.n>_bnd(T), for all q in [P_r(T)]^d
   *
   * RHS =
   *   [q=(1,0)]: -(xy, div (1,0))_T = 0
   *   [q=(y,0)]: -(xy, div (y,0))_T = 0
   *   [q=(x,0)]: -(xy, div (x,0))_T = -1/4
   *   [q=(0,1)]: -(xy, div (0,1))_T = 0
   *   [q=(0,y)]: -(xy, div (0,y))_T = -1/4
   *   [q=(0,x)]: -(xy, div (0,x))_T = 0
   *
   * Now we solve the system
   *   IP xi = RHS
   * This gives us the our solution as a linear combination of the q_i:
   *   sol = sum_i xi_i q_i
   *   where
   *     xi = [3/2 0 -3 3/2 -3 0]'
   */

  let (int_mon_wgrads, side_mon_wgrads) =
    wgrad_solver.wgrads_on_oshape([xy],    // interior
                                  [&[xy],  // left side
                                   &[xy],  // right
                                   &[xy],  // bottom
                                   &[xy]], // top
                                  OShape(0),
                                  rmesh);

  let xy_on_int_wgrad = &int_mon_wgrads[0];

  assert_eq!(xy_on_int_wgrad.comp_mon_coefs[0].as_slice(), &[3./2., 0., -3.]);
  assert_eq!(xy_on_int_wgrad.comp_mon_coefs[1].as_slice(), &[3./2., -3., 0.]);
}

// Tests below use the fact that if a polynomial p is in P_k(e) for some finite element e, where the
// weak gradient approximation space is [P_{k-1}(e)]^d, then wgrad(p) = grad(p) on the interior of e.
// Thus we can test weak gradient calculations on such polynomials and verify this equality. However,
// the weak gradient module only computes weak gradients of shape functions defined piecewise to be
// monomials on a single supporting face and 0 elsewhere. So to compute weak gradients of a polynomial
// across an entire finite element, we consider the polynomial as the sum of face-relative shape functions,
// and using the linearity of the weak gradient, we then compute the original polynomial's weak gradient
// as the sum of the weak gradients of these shape functions.

#[test]
fn test_wgrad_full_xy() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let mut wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(3), rmesh);

  let one = Mon2d { exps: [Deg(0), Deg(0)] };
  let x = Mon2d { exps: [Deg(1), Deg(0)] };
  let y = Mon2d { exps: [Deg(0), Deg(1)] };

  let (int_mon_wgrads, side_mon_wgrads) =
    wgrad_solver.wgrads_on_oshape([x*y],    // interior
                                  [&[one],  // left side (ignored because xy is 0 here)
                                   &[y],    // right
                                   &[one],  // bottom    (ignored ")
                                   &[x]],   // top
                                  OShape(0),
                                  rmesh);

  let xy_on_int_wgrad = &int_mon_wgrads[0];
  let y_on_right_side_wgrad = &side_mon_wgrads[1][0];
  let x_on_top_side_wgrad = &side_mon_wgrads[3][0];

  let wgrad = &lcomb_wgrads([(1., xy_on_int_wgrad), (1., y_on_right_side_wgrad), (1., x_on_top_side_wgrad)]);

  let wgrad_comp0 = PolyOwning::new(wgrad.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons.clone());
  let wgrad_comp1 = PolyOwning::new(wgrad.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons.clone());

  assert!(approx_equiv(&wgrad_comp0, &PolyOwning::new(~[1.],~[y]), 1e-9));
  assert!(approx_equiv(&wgrad_comp1, &PolyOwning::new(~[1.],~[x]), 1e-9));
}

#[test]
fn test_wgrad_full_x2y() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let mut wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(3), rmesh);

  let one = Mon2d { exps: [Deg(0), Deg(0)] };
  let x = Mon2d { exps: [Deg(1), Deg(0)] };
  let y = Mon2d { exps: [Deg(0), Deg(1)] };

  let (int_mon_wgrads, side_mon_wgrads) =
    wgrad_solver.wgrads_on_oshape([x*x*y],   // interior
                                  [&[one],   // left side (ignored because xy is 0 here)
                                   &[y],     // right
                                   &[one],   // bottom    (ignored ")
                                   &[x*x]],  // top
                                  OShape(0),
                                  rmesh);

  let x2y_on_int_wgrad = &int_mon_wgrads[0];
  let y_on_right_side_wgrad = &side_mon_wgrads[1][0];
  let x2_on_top_side_wgrad = &side_mon_wgrads[3][0];

  let wgrad = &lcomb_wgrads([(1., x2y_on_int_wgrad), (1., y_on_right_side_wgrad), (1., x2_on_top_side_wgrad)]);

  let wgrad_comp0 = PolyOwning::new(wgrad.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons.clone());
  let wgrad_comp1 = PolyOwning::new(wgrad.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons.clone());

  assert!(approx_equiv(&wgrad_comp0, &PolyOwning::new(~[2.],~[x*y]), 1e-9));
  assert!(approx_equiv(&wgrad_comp1, &PolyOwning::new(~[1.],~[x*x]), 1e-9));
}

#[test]
fn test_wgrad_dot() {
  let rmesh: ~RectMesh<Mon2d> = RectMesh::new(~[0f64, 0.],
                                              ~[3f64, 3.],
                                              ~[MeshCoord(3), MeshCoord(3)]);
  let wgrad_solver: WeakGradSolver<Mon2d> = WeakGradSolver::new(MaxMonDeg(2), rmesh);

  let wgrad1 = WeakGrad { comp_mon_coefs: ~[~[-2.5, 3., 0., 15., -6., -15.], ~[-2.5, 15., -15., 3., -6., 0.]] };
  let wgrad2 = WeakGrad { comp_mon_coefs: ~[~[2.5, -2., -0., -15., 6., 15.], ~[0., 0., 0., 0., 0., 0.]] };

  let mut wgrad_ops = wgrad_solver.weak_grad_ops();

  let fast_dot_prod = wgrad_ops.dot(&wgrad1, &wgrad2);

  // Compiler prevents second call of dot() which would have silently overwritten the existing wgrad's shared coefficients buffer. Beautiful!
  // let wgrad_x_top_dot_wgrad_y_right = wgrad_ops.dot(&wgrad1, &wgrad1);
  //   =>  error: cannot borrow `*wgrad_ops` as mutable more than once at a time

  let p1_0 = PolyBorrowingMons::new(wgrad1.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons);
  let p1_1 = PolyBorrowingMons::new(wgrad1.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons);
  let p2_0 = PolyBorrowingMons::new(wgrad2.comp_mon_coefs[0].clone(), wgrad_solver.wgrad_comp_mons);
  let p2_1 = PolyBorrowingMons::new(wgrad2.comp_mon_coefs[1].clone(), wgrad_solver.wgrad_comp_mons);
  let polys_dot_prod = PolyOwning::from_polys_lcomb([(1., &polynomial::mul(&p1_0, &p2_0)), (1., &polynomial::mul(&p1_1, &p2_1))]);

  println!("fast dot product:  {}", fast_dot_prod.to_str());
  println!("polys dot product: {}", polys_dot_prod.to_str());

  let canon_fast_dot_prod = fast_dot_prod.canonical_form();
  assert_eq!(&canon_fast_dot_prod.coefs, &polys_dot_prod.coefs);
  assert_eq!(&canon_fast_dot_prod.mons, &polys_dot_prod.mons);
}

fn lcomb_wgrads(terms: &[(R,&WeakGrad)]) -> WeakGrad {
  if terms.len() == 0 { fail!("lcomb_wgrads: At least one weak gradient is required.") }
  let (space_dims, num_comp_mons) = match terms[0] { (_, wgrad) => (wgrad.comp_mon_coefs.len(), wgrad.comp_mon_coefs[0].len()) };
  WeakGrad {
    comp_mon_coefs:
      vec::from_fn(space_dims, |d|
        vec::from_fn(num_comp_mons, |mon_num|
          terms.iter().fold(0 as R, |sum, &(c, wgrad)| sum + c * wgrad.comp_mon_coefs[d][mon_num])))
  }
}

<|endoftext|>