<commit_before>//! Functionality related to publishing a new crate or version of a crate.

use hex::ToHex;
use std::sync::Arc;
use swirl::Job;

use crate::controllers::cargo_prelude::*;
use crate::git;
use crate::models::dependency;
use crate::models::{
    insert_version_owner_action, Badge, Category, Keyword, NewCrate, NewVersion, Rights,
    VersionAction,
};

use crate::render;
use crate::util::{read_fill, read_le_u32, Maximums};
use crate::views::{EncodableCrateUpload, GoodCrate, PublishWarnings};

pub const MISSING_RIGHTS_ERROR_MESSAGE: &str =
    "this crate exists but you don't seem to be an owner. \
     If you believe this is a mistake, perhaps you need \
     to accept an invitation to be an owner before \
     publishing.";

/// Handles the `PUT /crates/new` route.
/// Used by `cargo publish` to publish a new crate or to publish a new version of an
/// existing crate.
///
/// Currently blocks the HTTP thread, perhaps some function calls can spawn new
/// threads and return completion or error through other methods  a `cargo publish
/// --status` command, via crates.io's front end, or email.
pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
    let app = Arc::clone(req.app());

    // The format of the req.body() of a publish request is as follows:
    //
    // metadata length
    // metadata in JSON about the crate being published
    // .crate tarball length
    // .crate tarball file
    //
    // - The metadata is read and interpreted in the parse_new_headers function.
    // - The .crate tarball length is read in this function in order to save the size of the file
    //   in the version record in the database.
    // - Then the .crate tarball length is passed to the upload_crate function where the actual
    //   file is read and uploaded.

    let new_crate = parse_new_headers(req)?;

    req.log_metadata("crate_name", new_crate.name.to_string());
    req.log_metadata("crate_version", new_crate.vers.to_string());

    let conn = app.primary_database.get()?;
    let ids = req.authenticate()?;
    let api_token_id = ids.api_token_id();
    let user = ids.user();

    let verified_email_address = user.verified_email(&conn)?;
    let verified_email_address = verified_email_address.ok_or_else(|| {
        cargo_err(&format!(
            "A verified email address is required to publish crates to crates.io. \
             Visit https://{}/me to set and verify your email address.",
            app.config.domain_name,
        ))
    })?;

    // Create a transaction on the database, if there are no errors,
    // commit the transactions to record a new or updated crate.
    conn.transaction(|| {
        let name = new_crate.name;
        let vers = &*new_crate.vers;
        let links = new_crate.links;
        let repo = new_crate.repository;
        let features = new_crate
            .features
            .into_iter()
            .map(|(k, v)| (k.0, v.into_iter().map(|v| v.0).collect()))
            .collect();
        let keywords = new_crate
            .keywords
            .iter()
            .map(|s| s.as_str())
            .collect::<Vec<_>>();
        let categories = new_crate
            .categories
            .iter()
            .map(|s| s.as_str())
            .collect::<Vec<_>>();

        // Persist the new crate, if it doesn't already exist
        let persist = NewCrate {
            name: &name,
            description: new_crate.description.as_deref(),
            homepage: new_crate.homepage.as_deref(),
            documentation: new_crate.documentation.as_deref(),
            readme: new_crate.readme.as_deref(),
            repository: repo.as_deref(),
            max_upload_size: None,
        };

        let license_file = new_crate.license_file.as_deref();
        let krate =
            persist.create_or_update(&conn, user.id, Some(&app.config.publish_rate_limit))?;

        let owners = krate.owners(&conn)?;
        if user.rights(req.app(), &owners)? < Rights::Publish {
            return Err(cargo_err(MISSING_RIGHTS_ERROR_MESSAGE));
        }

        if krate.name != *name {
            return Err(cargo_err(&format_args!(
                "crate was previously named `{}`",
                krate.name
            )));
        }

        // Length of the .crate tarball, which appears after the metadata in the request body.
        // TODO: Not sure why we're using the total content length (metadata + .crate file length)
        // to compare against the max upload size... investigate that and perhaps change to use
        // this file length.
        let file_length = read_le_u32(req.body())?;

        let content_length = req
            .content_length()
            .chain_error(|| cargo_err("missing header: Content-Length"))?;

        let maximums = Maximums::new(
            krate.max_upload_size,
            app.config.max_upload_size,
            app.config.max_unpack_size,
        );

        if content_length > maximums.max_upload_size {
            return Err(cargo_err(&format_args!(
                "max upload size is: {}",
                maximums.max_upload_size
            )));
        }

        // This is only redundant for now. Eventually the duplication will be removed.
        let license = new_crate.license.clone();

        // Persist the new version of this crate
        let version = NewVersion::new(
            krate.id,
            vers,
            &features,
            license,
            license_file,
            // Downcast is okay because the file length must be less than the max upload size
            // to get here, and max upload sizes are way less than i32 max
            file_length as i32,
            user.id,
        )?
        .save(&conn, &new_crate.authors, &verified_email_address)?;

        insert_version_owner_action(
            &conn,
            version.id,
            user.id,
            api_token_id,
            VersionAction::Publish,
        )?;

        // Link this new version to all dependencies
        let git_deps = dependency::add_dependencies(&conn, &new_crate.deps, version.id)?;

        // Update all keywords for this crate
        Keyword::update_crate(&conn, &krate, &keywords)?;

        // Update all categories for this crate, collecting any invalid categories
        // in order to be able to warn about them
        let ignored_invalid_categories = Category::update_crate(&conn, &krate, &categories)?;

        // Update all badges for this crate, collecting any invalid badges in
        // order to be able to warn about them
        let ignored_invalid_badges = Badge::update_crate(&conn, &krate, new_crate.badges.as_ref())?;
        let top_versions = krate.top_versions(&conn)?;

        if let Some(readme) = new_crate.readme {
            render::render_and_upload_readme(
                version.id,
                readme,
                new_crate
                    .readme_file
                    .unwrap_or_else(|| String::from("README.md")),
                repo,
            )
            .enqueue(&conn)?;
        }

        let cksum = app
            .config
            .uploader
            .upload_crate(req, &krate, maximums, vers)?;

        let hex_cksum = cksum.encode_hex::<String>();

        // Register this crate in our local git repo.
        let git_crate = git::Crate {
            name: name.0,
            vers: vers.to_string(),
            cksum: hex_cksum,
            features,
            deps: git_deps,
            yanked: Some(false),
            links,
        };
        git::add_crate(git_crate).enqueue(&conn)?;

        // The `other` field on `PublishWarnings` was introduced to handle a temporary warning
        // that is no longer needed. As such, crates.io currently does not return any `other`
        // warnings at this time, but if we need to, the field is available.
        let warnings = PublishWarnings {
            invalid_categories: ignored_invalid_categories,
            invalid_badges: ignored_invalid_badges,
            other: vec![],
        };

        Ok(req.json(&GoodCrate {
            krate: krate.minimal_encodable(&top_versions, None, false, None),
            warnings,
        }))
    })
}

/// Used by the `krate::new` function.
///
/// This function parses the JSON headers to interpret the data and validates
/// the data during and after the parsing. Returns crate metadata.
fn parse_new_headers(req: &mut dyn RequestExt) -> AppResult<EncodableCrateUpload> {
    // Read the json upload request
    let metadata_length = u64::from(read_le_u32(req.body())?);
    req.log_metadata("metadata_length", metadata_length);

    let max = req.app().config.max_upload_size;
    if metadata_length > max {
        return Err(cargo_err(&format_args!("max upload size is: {}", max)));
    }
    let mut json = vec![0; metadata_length as usize];
    read_fill(req.body(), &mut json)?;
    let json = String::from_utf8(json).map_err(|_| cargo_err("json body was not valid utf-8"))?;
    let new: EncodableCrateUpload = serde_json::from_str(&json)
        .map_err(|e| cargo_err(&format_args!("invalid upload request: {}", e)))?;

    // Make sure required fields are provided
    fn empty(s: Option<&String>) -> bool {
        s.map_or(true, String::is_empty)
    }

    // It can have up to three elements per below conditions.
    let mut missing = Vec::with_capacity(3);

    if empty(new.description.as_ref()) {
        missing.push("description");
    }
    if empty(new.license.as_ref()) && empty(new.license_file.as_ref()) {
        missing.push("license");
    }
    if new.authors.iter().all(String::is_empty) {
        missing.push("authors");
    }
    if !missing.is_empty() {
        return Err(cargo_err(&format_args!(
            "missing or empty metadata fields: {}. Please \
             see https://doc.rust-lang.org/cargo/reference/manifest.html for \
             how to upload metadata",
            missing.join(", ")
        )));
    }

    Ok(new)
}
<commit_msg>controllers/krate/publish: Extract `missing_metadata_error_message` function<commit_after>//! Functionality related to publishing a new crate or version of a crate.

use hex::ToHex;
use std::sync::Arc;
use swirl::Job;

use crate::controllers::cargo_prelude::*;
use crate::git;
use crate::models::dependency;
use crate::models::{
    insert_version_owner_action, Badge, Category, Keyword, NewCrate, NewVersion, Rights,
    VersionAction,
};

use crate::render;
use crate::util::{read_fill, read_le_u32, Maximums};
use crate::views::{EncodableCrateUpload, GoodCrate, PublishWarnings};

pub const MISSING_RIGHTS_ERROR_MESSAGE: &str =
    "this crate exists but you don't seem to be an owner. \
     If you believe this is a mistake, perhaps you need \
     to accept an invitation to be an owner before \
     publishing.";

/// Handles the `PUT /crates/new` route.
/// Used by `cargo publish` to publish a new crate or to publish a new version of an
/// existing crate.
///
/// Currently blocks the HTTP thread, perhaps some function calls can spawn new
/// threads and return completion or error through other methods  a `cargo publish
/// --status` command, via crates.io's front end, or email.
pub fn publish(req: &mut dyn RequestExt) -> EndpointResult {
    let app = Arc::clone(req.app());

    // The format of the req.body() of a publish request is as follows:
    //
    // metadata length
    // metadata in JSON about the crate being published
    // .crate tarball length
    // .crate tarball file
    //
    // - The metadata is read and interpreted in the parse_new_headers function.
    // - The .crate tarball length is read in this function in order to save the size of the file
    //   in the version record in the database.
    // - Then the .crate tarball length is passed to the upload_crate function where the actual
    //   file is read and uploaded.

    let new_crate = parse_new_headers(req)?;

    req.log_metadata("crate_name", new_crate.name.to_string());
    req.log_metadata("crate_version", new_crate.vers.to_string());

    let conn = app.primary_database.get()?;
    let ids = req.authenticate()?;
    let api_token_id = ids.api_token_id();
    let user = ids.user();

    let verified_email_address = user.verified_email(&conn)?;
    let verified_email_address = verified_email_address.ok_or_else(|| {
        cargo_err(&format!(
            "A verified email address is required to publish crates to crates.io. \
             Visit https://{}/me to set and verify your email address.",
            app.config.domain_name,
        ))
    })?;

    // Create a transaction on the database, if there are no errors,
    // commit the transactions to record a new or updated crate.
    conn.transaction(|| {
        let name = new_crate.name;
        let vers = &*new_crate.vers;
        let links = new_crate.links;
        let repo = new_crate.repository;
        let features = new_crate
            .features
            .into_iter()
            .map(|(k, v)| (k.0, v.into_iter().map(|v| v.0).collect()))
            .collect();
        let keywords = new_crate
            .keywords
            .iter()
            .map(|s| s.as_str())
            .collect::<Vec<_>>();
        let categories = new_crate
            .categories
            .iter()
            .map(|s| s.as_str())
            .collect::<Vec<_>>();

        // Persist the new crate, if it doesn't already exist
        let persist = NewCrate {
            name: &name,
            description: new_crate.description.as_deref(),
            homepage: new_crate.homepage.as_deref(),
            documentation: new_crate.documentation.as_deref(),
            readme: new_crate.readme.as_deref(),
            repository: repo.as_deref(),
            max_upload_size: None,
        };

        let license_file = new_crate.license_file.as_deref();
        let krate =
            persist.create_or_update(&conn, user.id, Some(&app.config.publish_rate_limit))?;

        let owners = krate.owners(&conn)?;
        if user.rights(req.app(), &owners)? < Rights::Publish {
            return Err(cargo_err(MISSING_RIGHTS_ERROR_MESSAGE));
        }

        if krate.name != *name {
            return Err(cargo_err(&format_args!(
                "crate was previously named `{}`",
                krate.name
            )));
        }

        // Length of the .crate tarball, which appears after the metadata in the request body.
        // TODO: Not sure why we're using the total content length (metadata + .crate file length)
        // to compare against the max upload size... investigate that and perhaps change to use
        // this file length.
        let file_length = read_le_u32(req.body())?;

        let content_length = req
            .content_length()
            .chain_error(|| cargo_err("missing header: Content-Length"))?;

        let maximums = Maximums::new(
            krate.max_upload_size,
            app.config.max_upload_size,
            app.config.max_unpack_size,
        );

        if content_length > maximums.max_upload_size {
            return Err(cargo_err(&format_args!(
                "max upload size is: {}",
                maximums.max_upload_size
            )));
        }

        // This is only redundant for now. Eventually the duplication will be removed.
        let license = new_crate.license.clone();

        // Persist the new version of this crate
        let version = NewVersion::new(
            krate.id,
            vers,
            &features,
            license,
            license_file,
            // Downcast is okay because the file length must be less than the max upload size
            // to get here, and max upload sizes are way less than i32 max
            file_length as i32,
            user.id,
        )?
        .save(&conn, &new_crate.authors, &verified_email_address)?;

        insert_version_owner_action(
            &conn,
            version.id,
            user.id,
            api_token_id,
            VersionAction::Publish,
        )?;

        // Link this new version to all dependencies
        let git_deps = dependency::add_dependencies(&conn, &new_crate.deps, version.id)?;

        // Update all keywords for this crate
        Keyword::update_crate(&conn, &krate, &keywords)?;

        // Update all categories for this crate, collecting any invalid categories
        // in order to be able to warn about them
        let ignored_invalid_categories = Category::update_crate(&conn, &krate, &categories)?;

        // Update all badges for this crate, collecting any invalid badges in
        // order to be able to warn about them
        let ignored_invalid_badges = Badge::update_crate(&conn, &krate, new_crate.badges.as_ref())?;
        let top_versions = krate.top_versions(&conn)?;

        if let Some(readme) = new_crate.readme {
            render::render_and_upload_readme(
                version.id,
                readme,
                new_crate
                    .readme_file
                    .unwrap_or_else(|| String::from("README.md")),
                repo,
            )
            .enqueue(&conn)?;
        }

        let cksum = app
            .config
            .uploader
            .upload_crate(req, &krate, maximums, vers)?;

        let hex_cksum = cksum.encode_hex::<String>();

        // Register this crate in our local git repo.
        let git_crate = git::Crate {
            name: name.0,
            vers: vers.to_string(),
            cksum: hex_cksum,
            features,
            deps: git_deps,
            yanked: Some(false),
            links,
        };
        git::add_crate(git_crate).enqueue(&conn)?;

        // The `other` field on `PublishWarnings` was introduced to handle a temporary warning
        // that is no longer needed. As such, crates.io currently does not return any `other`
        // warnings at this time, but if we need to, the field is available.
        let warnings = PublishWarnings {
            invalid_categories: ignored_invalid_categories,
            invalid_badges: ignored_invalid_badges,
            other: vec![],
        };

        Ok(req.json(&GoodCrate {
            krate: krate.minimal_encodable(&top_versions, None, false, None),
            warnings,
        }))
    })
}

/// Used by the `krate::new` function.
///
/// This function parses the JSON headers to interpret the data and validates
/// the data during and after the parsing. Returns crate metadata.
fn parse_new_headers(req: &mut dyn RequestExt) -> AppResult<EncodableCrateUpload> {
    // Read the json upload request
    let metadata_length = u64::from(read_le_u32(req.body())?);
    req.log_metadata("metadata_length", metadata_length);

    let max = req.app().config.max_upload_size;
    if metadata_length > max {
        return Err(cargo_err(&format_args!("max upload size is: {}", max)));
    }
    let mut json = vec![0; metadata_length as usize];
    read_fill(req.body(), &mut json)?;
    let json = String::from_utf8(json).map_err(|_| cargo_err("json body was not valid utf-8"))?;
    let new: EncodableCrateUpload = serde_json::from_str(&json)
        .map_err(|e| cargo_err(&format_args!("invalid upload request: {}", e)))?;

    // Make sure required fields are provided
    fn empty(s: Option<&String>) -> bool {
        s.map_or(true, String::is_empty)
    }

    // It can have up to three elements per below conditions.
    let mut missing = Vec::with_capacity(3);

    if empty(new.description.as_ref()) {
        missing.push("description");
    }
    if empty(new.license.as_ref()) && empty(new.license_file.as_ref()) {
        missing.push("license");
    }
    if new.authors.iter().all(String::is_empty) {
        missing.push("authors");
    }
    if !missing.is_empty() {
        let message = missing_metadata_error_message(&missing);
        return Err(cargo_err(&message));
    }

    Ok(new)
}

pub fn missing_metadata_error_message(missing: &[&str]) -> String {
    format!(
        "missing or empty metadata fields: {}. Please \
         see https://doc.rust-lang.org/cargo/reference/manifest.html for \
         how to upload metadata",
        missing.join(", ")
    )
}

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

    #[test]
    fn missing_metadata_error_message_test() {
        assert_eq!(missing_metadata_error_message(&["a"]), "missing or empty metadata fields: a. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata");
        assert_eq!(missing_metadata_error_message(&["a", "b"]), "missing or empty metadata fields: a, b. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata");
        assert_eq!(missing_metadata_error_message(&["a", "b", "c"]), "missing or empty metadata fields: a, b, c. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata");
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use rustc_front::hir::*;
use syntax::ast::Name;
use syntax::ptr::P;
use syntax::codemap::{Span, Spanned};
use rustc::middle::def_id::DefId;
use rustc::middle::ty::{self, MethodTraitItemId, ImplOrTraitItemId};

use syntax::ast::{Lit, LitKind};

use utils::{get_item_name, snippet, span_lint, span_lint_and_then, walk_ptrs_ty};

/// **What it does:** This lint checks for getting the length of something via `.len()` just to compare to zero, and suggests using `.is_empty()` where applicable.
///
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster than calculating their length. So it is good to get into the habit of using `.is_empty()`, and having it is cheap. Besides, it makes the intent clearer than a comparison.
///
/// **Known problems:** None
///
/// **Example:** `if x.len() == 0 { .. }`
declare_lint! {
    pub LEN_ZERO, Warn,
    "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` \
     could be used instead"
}

/// **What it does:** This lint checks for items that implement `.len()` but not `.is_empty()`.
///
/// **Why is this bad?** It is good custom to have both methods, because for some data structures, asking about the length will be a costly operation, whereas `.is_empty()` can usually answer in constant time. Also it used to lead to false positives on the [`len_zero`](#len_zero) lint – currently that lint will ignore such entities.
///
/// **Known problems:** None
///
/// **Example:**
/// ```
/// impl X {
///     fn len(&self) -> usize { .. }
/// }
/// ```
declare_lint! {
    pub LEN_WITHOUT_IS_EMPTY, Warn,
    "traits and impls that have `.len()` but not `.is_empty()`"
}

#[derive(Copy,Clone)]
pub struct LenZero;

impl LintPass for LenZero {
    fn get_lints(&self) -> LintArray {
        lint_array!(LEN_ZERO, LEN_WITHOUT_IS_EMPTY)
    }
}

impl LateLintPass for LenZero {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        match item.node {
            ItemTrait(_, _, _, ref trait_items) => check_trait_items(cx, item, trait_items),
            ItemImpl(_, _, _, None, _, ref impl_items) => check_impl_items(cx, item, impl_items),
            _ => (),
        }
    }

    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if let ExprBinary(Spanned{node: cmp, ..}, ref left, ref right) = expr.node {
            match cmp {
                BiEq => check_cmp(cx, expr.span, left, right, ""),
                BiGt | BiNe => check_cmp(cx, expr.span, left, right, "!"),
                _ => (),
            }
        }
    }
}

fn check_trait_items(cx: &LateContext, item: &Item, trait_items: &[TraitItem]) {
    fn is_named_self(item: &TraitItem, name: &str) -> bool {
        item.name.as_str() == name &&
        if let MethodTraitItem(ref sig, _) = item.node {
            is_self_sig(sig)
        } else {
            false
        }
    }

    if !trait_items.iter().any(|i| is_named_self(i, "is_empty")) {
        for i in trait_items {
            if is_named_self(i, "len") {
                span_lint(cx,
                          LEN_WITHOUT_IS_EMPTY,
                          i.span,
                          &format!("trait `{}` has a `.len(_: &Self)` method, but no `.is_empty(_: &Self)` method. \
                                    Consider adding one",
                                   item.name));
            }
        }
    }
}

fn check_impl_items(cx: &LateContext, item: &Item, impl_items: &[ImplItem]) {
    fn is_named_self(item: &ImplItem, name: &str) -> bool {
        item.name.as_str() == name &&
        if let ImplItemKind::Method(ref sig, _) = item.node {
            is_self_sig(sig)
        } else {
            false
        }
    }

    if !impl_items.iter().any(|i| is_named_self(i, "is_empty")) {
        for i in impl_items {
            if is_named_self(i, "len") {
                let s = i.span;
                span_lint(cx,
                          LEN_WITHOUT_IS_EMPTY,
                          Span {
                              lo: s.lo,
                              hi: s.lo,
                              expn_id: s.expn_id,
                          },
                          &format!("item `{}` has a `.len(_: &Self)` method, but no `.is_empty(_: &Self)` method. \
                                    Consider adding one",
                                   item.name));
                return;
            }
        }
    }
}

fn is_self_sig(sig: &MethodSig) -> bool {
    if let SelfStatic = sig.explicit_self.node {
        false
    } else {
        sig.decl.inputs.len() == 1
    }
}

fn check_cmp(cx: &LateContext, span: Span, left: &Expr, right: &Expr, op: &str) {
    // check if we are in an is_empty() method
    if let Some(name) = get_item_name(cx, left) {
        if name.as_str() == "is_empty" {
            return;
        }
    }
    match (&left.node, &right.node) {
        (&ExprLit(ref lit), &ExprMethodCall(ref method, _, ref args)) |
        (&ExprMethodCall(ref method, _, ref args), &ExprLit(ref lit)) => {
            check_len_zero(cx, span, &method.node, args, lit, op)
        }
        _ => (),
    }
}

fn check_len_zero(cx: &LateContext, span: Span, name: &Name, args: &[P<Expr>], lit: &Lit, op: &str) {
    if let Spanned{node: LitKind::Int(0, _), ..} = *lit {
        if name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
            span_lint_and_then(cx,
                               LEN_ZERO,
                               span,
                               "length comparison to zero",
                               |db| {
                                   db.span_suggestion(span,
                                                      "consider using `is_empty`",
                                                       format!("{}{}.is_empty()",
                                                               op,
                                                               snippet(cx, args[0].span, "_")));
                               });
        }
    }
}

/// check if this type has an is_empty method
fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
    /// get a ImplOrTraitItem and return true if it matches is_empty(self)
    fn is_is_empty(cx: &LateContext, id: &ImplOrTraitItemId) -> bool {
        if let MethodTraitItemId(def_id) = *id {
            if let ty::MethodTraitItem(ref method) = cx.tcx.impl_or_trait_item(def_id) {
                method.name.as_str() == "is_empty" && method.fty.sig.skip_binder().inputs.len() == 1
            } else {
                false
            }
        } else {
            false
        }
    }

    /// check the inherent impl's items for an is_empty(self) method
    fn has_is_empty_impl(cx: &LateContext, id: &DefId) -> bool {
        let impl_items = cx.tcx.impl_items.borrow();
        cx.tcx.inherent_impls.borrow().get(id).map_or(false, |ids| {
            ids.iter().any(|iid| impl_items.get(iid).map_or(false, |iids| iids.iter().any(|i| is_is_empty(cx, i))))
        })
    }

    let ty = &walk_ptrs_ty(&cx.tcx.expr_ty(expr));
    match ty.sty {
        ty::TyTrait(_) => {
            cx.tcx
              .trait_item_def_ids
              .borrow()
              .get(&ty.ty_to_def_id().expect("trait impl not found"))
              .map_or(false, |ids| ids.iter().any(|i| is_is_empty(cx, i)))
        }
        ty::TyProjection(_) => ty.ty_to_def_id().map_or(false, |id| has_is_empty_impl(cx, &id)),
        ty::TyEnum(ref id, _) | ty::TyStruct(ref id, _) => has_is_empty_impl(cx, &id.did),
        ty::TyArray(..) => true,
        _ => false,
    }
}
<commit_msg>Macro check `len_zero`<commit_after>use rustc::lint::*;
use rustc_front::hir::*;
use syntax::ast::Name;
use syntax::ptr::P;
use syntax::codemap::{Span, Spanned};
use rustc::middle::def_id::DefId;
use rustc::middle::ty::{self, MethodTraitItemId, ImplOrTraitItemId};

use syntax::ast::{Lit, LitKind};

use utils::{get_item_name, in_macro, snippet, span_lint, span_lint_and_then, walk_ptrs_ty};

/// **What it does:** This lint checks for getting the length of something via `.len()` just to compare to zero, and suggests using `.is_empty()` where applicable.
///
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster than calculating their length. So it is good to get into the habit of using `.is_empty()`, and having it is cheap. Besides, it makes the intent clearer than a comparison.
///
/// **Known problems:** None
///
/// **Example:** `if x.len() == 0 { .. }`
declare_lint! {
    pub LEN_ZERO, Warn,
    "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` \
     could be used instead"
}

/// **What it does:** This lint checks for items that implement `.len()` but not `.is_empty()`.
///
/// **Why is this bad?** It is good custom to have both methods, because for some data structures, asking about the length will be a costly operation, whereas `.is_empty()` can usually answer in constant time. Also it used to lead to false positives on the [`len_zero`](#len_zero) lint – currently that lint will ignore such entities.
///
/// **Known problems:** None
///
/// **Example:**
/// ```
/// impl X {
///     fn len(&self) -> usize { .. }
/// }
/// ```
declare_lint! {
    pub LEN_WITHOUT_IS_EMPTY, Warn,
    "traits and impls that have `.len()` but not `.is_empty()`"
}

#[derive(Copy,Clone)]
pub struct LenZero;

impl LintPass for LenZero {
    fn get_lints(&self) -> LintArray {
        lint_array!(LEN_ZERO, LEN_WITHOUT_IS_EMPTY)
    }
}

impl LateLintPass for LenZero {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        if in_macro(cx, item.span) {
            return;
        }

        match item.node {
            ItemTrait(_, _, _, ref trait_items) => check_trait_items(cx, item, trait_items),
            ItemImpl(_, _, _, None, _, ref impl_items) => check_impl_items(cx, item, impl_items),
            _ => (),
        }
    }

    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        if in_macro(cx, expr.span) {
            return;
        }

        if let ExprBinary(Spanned{node: cmp, ..}, ref left, ref right) = expr.node {
            match cmp {
                BiEq => check_cmp(cx, expr.span, left, right, ""),
                BiGt | BiNe => check_cmp(cx, expr.span, left, right, "!"),
                _ => (),
            }
        }
    }
}

fn check_trait_items(cx: &LateContext, item: &Item, trait_items: &[TraitItem]) {
    fn is_named_self(item: &TraitItem, name: &str) -> bool {
        item.name.as_str() == name &&
        if let MethodTraitItem(ref sig, _) = item.node {
            is_self_sig(sig)
        } else {
            false
        }
    }

    if !trait_items.iter().any(|i| is_named_self(i, "is_empty")) {
        for i in trait_items {
            if is_named_self(i, "len") {
                span_lint(cx,
                          LEN_WITHOUT_IS_EMPTY,
                          i.span,
                          &format!("trait `{}` has a `.len(_: &Self)` method, but no `.is_empty(_: &Self)` method. \
                                    Consider adding one",
                                   item.name));
            }
        }
    }
}

fn check_impl_items(cx: &LateContext, item: &Item, impl_items: &[ImplItem]) {
    fn is_named_self(item: &ImplItem, name: &str) -> bool {
        item.name.as_str() == name &&
        if let ImplItemKind::Method(ref sig, _) = item.node {
            is_self_sig(sig)
        } else {
            false
        }
    }

    if !impl_items.iter().any(|i| is_named_self(i, "is_empty")) {
        for i in impl_items {
            if is_named_self(i, "len") {
                let s = i.span;
                span_lint(cx,
                          LEN_WITHOUT_IS_EMPTY,
                          Span {
                              lo: s.lo,
                              hi: s.lo,
                              expn_id: s.expn_id,
                          },
                          &format!("item `{}` has a `.len(_: &Self)` method, but no `.is_empty(_: &Self)` method. \
                                    Consider adding one",
                                   item.name));
                return;
            }
        }
    }
}

fn is_self_sig(sig: &MethodSig) -> bool {
    if let SelfStatic = sig.explicit_self.node {
        false
    } else {
        sig.decl.inputs.len() == 1
    }
}

fn check_cmp(cx: &LateContext, span: Span, left: &Expr, right: &Expr, op: &str) {
    // check if we are in an is_empty() method
    if let Some(name) = get_item_name(cx, left) {
        if name.as_str() == "is_empty" {
            return;
        }
    }
    match (&left.node, &right.node) {
        (&ExprLit(ref lit), &ExprMethodCall(ref method, _, ref args)) |
        (&ExprMethodCall(ref method, _, ref args), &ExprLit(ref lit)) => {
            check_len_zero(cx, span, &method.node, args, lit, op)
        }
        _ => (),
    }
}

fn check_len_zero(cx: &LateContext, span: Span, name: &Name, args: &[P<Expr>], lit: &Lit, op: &str) {
    if let Spanned{node: LitKind::Int(0, _), ..} = *lit {
        if name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
            span_lint_and_then(cx,
                               LEN_ZERO,
                               span,
                               "length comparison to zero",
                               |db| {
                                   db.span_suggestion(span,
                                                      "consider using `is_empty`",
                                                       format!("{}{}.is_empty()",
                                                               op,
                                                               snippet(cx, args[0].span, "_")));
                               });
        }
    }
}

/// check if this type has an is_empty method
fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
    /// get a ImplOrTraitItem and return true if it matches is_empty(self)
    fn is_is_empty(cx: &LateContext, id: &ImplOrTraitItemId) -> bool {
        if let MethodTraitItemId(def_id) = *id {
            if let ty::MethodTraitItem(ref method) = cx.tcx.impl_or_trait_item(def_id) {
                method.name.as_str() == "is_empty" && method.fty.sig.skip_binder().inputs.len() == 1
            } else {
                false
            }
        } else {
            false
        }
    }

    /// check the inherent impl's items for an is_empty(self) method
    fn has_is_empty_impl(cx: &LateContext, id: &DefId) -> bool {
        let impl_items = cx.tcx.impl_items.borrow();
        cx.tcx.inherent_impls.borrow().get(id).map_or(false, |ids| {
            ids.iter().any(|iid| impl_items.get(iid).map_or(false, |iids| iids.iter().any(|i| is_is_empty(cx, i))))
        })
    }

    let ty = &walk_ptrs_ty(&cx.tcx.expr_ty(expr));
    match ty.sty {
        ty::TyTrait(_) => {
            cx.tcx
              .trait_item_def_ids
              .borrow()
              .get(&ty.ty_to_def_id().expect("trait impl not found"))
              .map_or(false, |ids| ids.iter().any(|i| is_is_empty(cx, i)))
        }
        ty::TyProjection(_) => ty.ty_to_def_id().map_or(false, |id| has_is_empty_impl(cx, &id)),
        ty::TyEnum(ref id, _) | ty::TyStruct(ref id, _) => has_is_empty_impl(cx, &id.did),
        ty::TyArray(..) => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>extern crate nom;
extern crate classfile_parser;

// use std::fs::File;
// use std::io::Read;

use nom::IResult;

use classfile_parser::{class_parser};

#[test]
fn test_valid_class() {
    let valid_class = include_bytes!("../assets/BasicClass.class");
    let res = class_parser(valid_class);
    match res {
        IResult::Done(_, c) => {
            println!("Valid class file, version {},{} const_pool({}), this=const[{}], super=const[{}], interfaces({}), fields({}), methods({}), attributes({})", c.major_version, c.minor_version, c.const_pool_size, c.this_class, c.super_class, c.interfaces_count, c.fields_count, c.methods_count, c.attributes_count);
            println!("Constant pool:");
            let mut const_index = 1;
            for cp in &c.const_pool {
                println!("\t[{}] = {}", const_index, cp.to_string());
                const_index += 1;
            }
            println!("Interfaces:");
            let mut interface_index = 0;
            for i in &c.interfaces {
                println!("\t[{}] = const[{}] = {}", interface_index, i, c.const_pool[(i-1) as usize].to_string());
                interface_index += 1;
            }
            println!("Fields:");
            let mut field_index = 0;
            for f in &c.fields {
                println!("\t[{}] Name(const[{}] = {})", field_index, f.name_index, c.const_pool[(f.name_index - 1) as usize].to_string());
                field_index += 1;
            }
            println!("Methods:");
            let mut method_index = 0;
            for m in &c.methods {
                println!("\t[{}] Name(const[{}] = {})", method_index, m.name_index, c.const_pool[(m.name_index - 1) as usize].to_string());
                m.get_code(&c);
                method_index += 1;
            }
        },
        _ => panic!("Not a class file"),
    };
}

// TODO - NP - Figure out file load path
// #[test]
// fn test_regular_use() {
//     let mut f = File::open("../assets/BasicClass.class").unwrap();
//     let mut buffer = vec![0; 10];
//     let read = f.read_to_end(&mut buffer);

//     let res = class_parser(&buffer);
//     match res {
//         IResult::Done(_, c) => {
//             println!("Valid class file, version {},{} const_pool({}), this=const[{}], super=const[{}], interfaces({}), fields({}), methods({}), attributes({})", c.major_version, c.minor_version, c.const_pool_size, c.this_class, c.super_class, c.interfaces_count, c.fields_count, c.methods_count, c.attributes_count);
//         },
//         _ => panic!("argh!"),
//     };
// }

#[test]
fn test_malformed_class() {
    let malformed_class = include_bytes!("../assets/malformed.class");
    let res = class_parser(malformed_class);
    match res {
        IResult::Done(_, _) => panic!("The file is not valid and shouldn't be parsed"),
        _ => res,
    };
}

// #[test]
// fn test_constant_utf8() {
//     let hello_world_data = &[
//         // 0x01, // tag = 1
//         0x00, 0x0C, // length = 12
//         0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21 // 'Hello world!' in UTF8
//     ];
//     let res = const_utf8(hello_world_data);

//     match res {
//         IResult::Done(_, c) =>
//         match c {
//             Constant::Utf8(ref s) =>
//                  println!("Valid UTF8 const: {}", s.utf8_string),
//             _ => panic!("It's a const, but of what type?")
//         },
//         _ => panic!("Not a UTF type const?"),
//     };
// }
<commit_msg>Removed call to deleted test function<commit_after>extern crate nom;
extern crate classfile_parser;

// use std::fs::File;
// use std::io::Read;

use nom::IResult;

use classfile_parser::{class_parser};

#[test]
fn test_valid_class() {
    let valid_class = include_bytes!("../assets/BasicClass.class");
    let res = class_parser(valid_class);
    match res {
        IResult::Done(_, c) => {
            println!("Valid class file, version {},{} const_pool({}), this=const[{}], super=const[{}], interfaces({}), fields({}), methods({}), attributes({})", c.major_version, c.minor_version, c.const_pool_size, c.this_class, c.super_class, c.interfaces_count, c.fields_count, c.methods_count, c.attributes_count);
            println!("Constant pool:");
            let mut const_index = 1;
            for cp in &c.const_pool {
                println!("\t[{}] = {}", const_index, cp.to_string());
                const_index += 1;
            }
            println!("Interfaces:");
            let mut interface_index = 0;
            for i in &c.interfaces {
                println!("\t[{}] = const[{}] = {}", interface_index, i, c.const_pool[(i-1) as usize].to_string());
                interface_index += 1;
            }
            println!("Fields:");
            let mut field_index = 0;
            for f in &c.fields {
                println!("\t[{}] Name(const[{}] = {})", field_index, f.name_index, c.const_pool[(f.name_index - 1) as usize].to_string());
                field_index += 1;
            }
            println!("Methods:");
            let mut method_index = 0;
            for m in &c.methods {
                println!("\t[{}] Name(const[{}] = {})", method_index, m.name_index, c.const_pool[(m.name_index - 1) as usize].to_string());
                method_index += 1;
            }
        },
        _ => panic!("Not a class file"),
    };
}

// TODO - NP - Figure out file load path
// #[test]
// fn test_regular_use() {
//     let mut f = File::open("../assets/BasicClass.class").unwrap();
//     let mut buffer = vec![0; 10];
//     let read = f.read_to_end(&mut buffer);

//     let res = class_parser(&buffer);
//     match res {
//         IResult::Done(_, c) => {
//             println!("Valid class file, version {},{} const_pool({}), this=const[{}], super=const[{}], interfaces({}), fields({}), methods({}), attributes({})", c.major_version, c.minor_version, c.const_pool_size, c.this_class, c.super_class, c.interfaces_count, c.fields_count, c.methods_count, c.attributes_count);
//         },
//         _ => panic!("argh!"),
//     };
// }

#[test]
fn test_malformed_class() {
    let malformed_class = include_bytes!("../assets/malformed.class");
    let res = class_parser(malformed_class);
    match res {
        IResult::Done(_, _) => panic!("The file is not valid and shouldn't be parsed"),
        _ => res,
    };
}

// #[test]
// fn test_constant_utf8() {
//     let hello_world_data = &[
//         // 0x01, // tag = 1
//         0x00, 0x0C, // length = 12
//         0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21 // 'Hello world!' in UTF8
//     ];
//     let res = const_utf8(hello_world_data);

//     match res {
//         IResult::Done(_, c) =>
//         match c {
//             Constant::Utf8(ref s) =>
//                  println!("Valid UTF8 const: {}", s.utf8_string),
//             _ => panic!("It's a const, but of what type?")
//         },
//         _ => panic!("Not a UTF type const?"),
//     };
// }
<|endoftext|>


<commit_before>use mio;
use crypto::{PasswordHash, SharedSecret};
use debug::as_hex;
use device::NetDevice;
use packet;
use PrivateIdentity;
use Router;

#[derive(Show)]
pub enum Task<'a> {
	HandleIncomingPacket(packet::CryptoAuth<'a>),
	HandleOutgoingPacket(packet::IPv6<'a>)
}

pub trait EventReceiver {
	fn register(&self, event_loop: &mut mio::EventLoop<usize, ()>, token: mio::Token)
	           -> mio::MioResult<()>;
	fn receive<'a>(&'a mut self, buffer: &'a mut [u8]) -> Option<Task>;
}

// TODO Show blocked by https://github.com/rust-lang/rust/issues/20676
// #[derive(Show)]
pub struct EventHandler<'a> {
	my_identity: PrivateIdentity,
	devices: Vec<Box<NetDevice + 'a>>,
	router: Router
}

impl<'a> EventHandler<'a> {
	pub fn new(my_identity: PrivateIdentity,
	           devices: Vec<Box<NetDevice + 'a>>,
	           router: Router) -> EventHandler<'a> {

		EventHandler {
			my_identity: my_identity,
			devices: devices,
			router: router
		}
	}

	pub fn register_handlers(&self, event_loop: &mut mio::EventLoop<usize, ()>)
	                         -> mio::MioResult<()> {
		for (i, device) in self.devices.iter().enumerate() {
			try!(device.register(event_loop, mio::Token(i)));
		}
		Ok(())
	}
}

impl<'a> mio::Handler<usize, ()> for EventHandler<'a> {
	fn readable(&mut self, _event_loop: &mut mio::EventLoop<usize, ()>,
	            token: mio::Token, _hint: mio::event::ReadHint) {

		let mut buffer = [0u8; 1500];

		let device_idx = token.as_usize();
		let maybe_task = self.devices[device_idx].receive(&mut buffer);

		if let Some(task) = maybe_task {
			match task {
				Task::HandleIncomingPacket(mut ca_packet) => {
					println!("Handling incoming packet");

					let password_hash = PasswordHash::from_password("aaa");

					let shared_secret = SharedSecret::with_password(
						&self.my_identity.private_key,
						&ca_packet.public_key(),
						&password_hash);

					match ca_packet.decrypt(&shared_secret) {
						Some(message) => println!("Decrypted message: {}", as_hex(message.as_slice())),
						None => println!("Couldn't decrypt the message!")
					}

				},
				Task::HandleOutgoingPacket(ipv6_packet) => {
					let destination = ipv6_packet.get_destination().unwrap();
					println!("Handling outgoing packet to {}", destination);

					let route = self.router.get_route(&destination);
					println!("    Route: {}", route);
				}
			}
		}
	}
}
<commit_msg>Run timer every second (and do nothing, for now)<commit_after>use std::time::duration::Duration;
use mio;
use crypto::{PasswordHash, SharedSecret};
use debug::as_hex;
use device::NetDevice;
use packet;
use PrivateIdentity;
use Router;

#[derive(Show)]
pub enum Task<'a> {
	HandleIncomingPacket(packet::CryptoAuth<'a>),
	HandleOutgoingPacket(packet::IPv6<'a>)
}

pub trait EventReceiver {
	fn register(&self, event_loop: &mut mio::EventLoop<usize, ()>, token: mio::Token)
	            -> mio::MioResult<()>;
	fn receive<'a>(&'a mut self, buffer: &'a mut [u8]) -> Option<Task>;
}

// TODO Show blocked by https://github.com/rust-lang/rust/issues/20676
// #[derive(Show)]
pub struct EventHandler<'a> {
	my_identity: PrivateIdentity,
	devices: Vec<Box<NetDevice + 'a>>,
	router: Router
}

impl<'a> EventHandler<'a> {
	pub fn new(my_identity: PrivateIdentity,
	           devices: Vec<Box<NetDevice + 'a>>,
	           router: Router) -> EventHandler<'a> {

		EventHandler {
			my_identity: my_identity,
			devices: devices,
			router: router
		}
	}

	pub fn register_handlers(&self, event_loop: &mut mio::EventLoop<usize, ()>)
	                         -> mio::MioResult<()> {
		for (i, device) in self.devices.iter().enumerate() {
			try!(device.register(event_loop, mio::Token(i)));
		}
		event_loop.timeout(1000, Duration::milliseconds(0)).unwrap();
		Ok(())
	}
}

impl<'a> mio::Handler<usize, ()> for EventHandler<'a> {
	fn timeout(&mut self, event_loop: &mut mio::EventLoop<usize, ()>, timeout: usize) {
		assert_eq!(timeout, 1000);

		event_loop.timeout(1000, Duration::milliseconds(1000)).unwrap();
	}

	fn readable(&mut self, _event_loop: &mut mio::EventLoop<usize, ()>,
	            token: mio::Token, _hint: mio::event::ReadHint) {

		let mut buffer = [0u8; 1500];

		let device_idx = token.as_usize();
		let maybe_task = self.devices[device_idx].receive(&mut buffer);

		if let Some(task) = maybe_task {
			match task {
				Task::HandleIncomingPacket(mut ca_packet) => {
					println!("Handling incoming packet");

					let password_hash = PasswordHash::from_password("aaa");

					let shared_secret = SharedSecret::with_password(
						&self.my_identity.private_key,
						&ca_packet.public_key(),
						&password_hash);

					match ca_packet.decrypt(&shared_secret) {
						Some(message) => println!("Decrypted message: {}", as_hex(message.as_slice())),
						None => println!("Couldn't decrypt the message!")
					}

				},
				Task::HandleOutgoingPacket(ipv6_packet) => {
					let destination = ipv6_packet.get_destination().unwrap();
					println!("Handling outgoing packet to {}", destination);

					let route = self.router.get_route(&destination);
					println!("    Route: {}", route);
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>extern crate ndarray;

use ndarray::{
    RcArray,
    Array,
    RemoveAxis,
    arr2,
    Axis,
    Dimension,
    Dim,
};

#[test]
fn remove_axis()
{
    assert_eq!(Dim([3]).remove_axis(Axis(0)), Dim([]));
    assert_eq!(Dim([1, 2]).remove_axis(Axis(0)), Dim([2]));
    assert_eq!(Dim([4, 5, 6]).remove_axis(Axis(1)), Dim([4, 6]));

    assert_eq!(Dim(vec![1,2]).remove_axis(Axis(0)), Dim(vec![2]));
    assert_eq!(Dim(vec![4, 5, 6]).remove_axis(Axis(1)), Dim(vec![4, 6]));

    let a = RcArray::<f32, _>::zeros((4,5));
    a.subview(Axis(1), 0);

    /*
    let a = RcArray::<f32, _>::zeros(vec![4,5,6]);
    let _b = a.into_subview(Axis(1), 0).reshape((4, 6)).reshape(vec![2, 3, 4]);
    */

}

#[test]
fn dyn_dimension()
{
    let a = arr2(&[[1., 2.], [3., 4.0]]).into_shape(vec![2, 2]).unwrap();
    assert_eq!(&a - &a, Array::zeros(vec![2, 2]));
    assert_eq!(a[&[0, 0][..]], 1.);
    assert_eq!(a[vec![0, 0]], 1.);

    let mut dim = vec![1; 1024];
    dim[16] = 4;
    dim[17] = 3;
    let z = Array::<f32, _>::zeros(dim.clone());
    assert_eq!(z.shape(), &dim[..]);
}

#[test]
fn fastest_varying_order() {
    let strides = Dim([2, 8, 4, 1]);
    let order = strides._fastest_varying_stride_order();
    assert_eq!(order.slice(), &[3, 0, 2, 1]);

    assert_eq!(Dim([1, 3])._fastest_varying_stride_order(), Dim([0, 1]));
    assert_eq!(Dim([7, 2])._fastest_varying_stride_order(), Dim([1, 0]));
    assert_eq!(Dim([6, 1, 3])._fastest_varying_stride_order(), Dim([1, 2, 0]));

    // it's important that it produces distinct indices. Prefer the stable order
    // where 0 is before 1 when they are equal.
    assert_eq!(Dim([2, 2])._fastest_varying_stride_order(), [0, 1]);
    assert_eq!(Dim([2, 2, 1])._fastest_varying_stride_order(), [2, 0, 1]);
    assert_eq!(Dim([2, 2, 3, 1, 2])._fastest_varying_stride_order(), [3, 0, 1, 4, 2]);
}

#[test]
fn test_indexing() {
    let mut x = Dim([1, 2]);

    assert_eq!(x[0], 1);
    assert_eq!(x[1], 2);

    x[0] = 7;
    assert_eq!(x, [7, 2]);
}

#[test]
fn test_operations() {
    let mut x = Dim([1, 2]);
    let mut y = Dim([1, 1]);

    assert_eq!(x + y, [2, 3]);

    x += y;
    assert_eq!(x, [2, 3]);
    x *= 2;
    assert_eq!(x, [4, 6]);

    y[0] -= 1;
    assert_eq!(y, [0, 1]);
}

#[test]
fn test_generic_operations() {
    fn test_dim<D: Dimension>(d: &D) {
        let mut x = d.clone();
        x[0] += 1;
        assert_eq!(x[0], 3);
        x += d;
        assert_eq!(x[0], 5);
    }

    test_dim(&Dim([2, 3, 4]));
    test_dim(&Dim(vec![2, 3, 4, 1]));
    test_dim(&Dim(2));
}

#[test]
fn test_array_view() {
    fn test_dim<D: Dimension>(d: &D) {
        assert_eq!(d.as_array_view().scalar_sum(), 7);
        assert_eq!(d.as_array_view().strides(), &[1]);
    }

    test_dim(&Dim([1, 2, 4]));
    test_dim(&Dim(vec![1, 1, 2, 3]));
    test_dim(&Dim(7));
}
<commit_msg>Reenable a dimension test<commit_after>extern crate ndarray;

use ndarray::{
    RcArray,
    Array,
    RemoveAxis,
    arr2,
    Axis,
    Dimension,
    Dim,
};

#[test]
fn remove_axis()
{
    assert_eq!(Dim([3]).remove_axis(Axis(0)), Dim([]));
    assert_eq!(Dim([1, 2]).remove_axis(Axis(0)), Dim([2]));
    assert_eq!(Dim([4, 5, 6]).remove_axis(Axis(1)), Dim([4, 6]));

    assert_eq!(Dim(vec![1,2]).remove_axis(Axis(0)), Dim(vec![2]));
    assert_eq!(Dim(vec![4, 5, 6]).remove_axis(Axis(1)), Dim(vec![4, 6]));

    let a = RcArray::<f32, _>::zeros((4,5));
    a.subview(Axis(1), 0);

    let a = RcArray::<f32, _>::zeros(vec![4,5,6]);
    let _b = a.into_subview(Axis(1), 0).reshape((4, 6)).reshape(vec![2, 3, 4]);
}

#[test]
fn dyn_dimension()
{
    let a = arr2(&[[1., 2.], [3., 4.0]]).into_shape(vec![2, 2]).unwrap();
    assert_eq!(&a - &a, Array::zeros(vec![2, 2]));
    assert_eq!(a[&[0, 0][..]], 1.);
    assert_eq!(a[vec![0, 0]], 1.);

    let mut dim = vec![1; 1024];
    dim[16] = 4;
    dim[17] = 3;
    let z = Array::<f32, _>::zeros(dim.clone());
    assert_eq!(z.shape(), &dim[..]);
}

#[test]
fn fastest_varying_order() {
    let strides = Dim([2, 8, 4, 1]);
    let order = strides._fastest_varying_stride_order();
    assert_eq!(order.slice(), &[3, 0, 2, 1]);

    assert_eq!(Dim([1, 3])._fastest_varying_stride_order(), Dim([0, 1]));
    assert_eq!(Dim([7, 2])._fastest_varying_stride_order(), Dim([1, 0]));
    assert_eq!(Dim([6, 1, 3])._fastest_varying_stride_order(), Dim([1, 2, 0]));

    // it's important that it produces distinct indices. Prefer the stable order
    // where 0 is before 1 when they are equal.
    assert_eq!(Dim([2, 2])._fastest_varying_stride_order(), [0, 1]);
    assert_eq!(Dim([2, 2, 1])._fastest_varying_stride_order(), [2, 0, 1]);
    assert_eq!(Dim([2, 2, 3, 1, 2])._fastest_varying_stride_order(), [3, 0, 1, 4, 2]);
}

#[test]
fn test_indexing() {
    let mut x = Dim([1, 2]);

    assert_eq!(x[0], 1);
    assert_eq!(x[1], 2);

    x[0] = 7;
    assert_eq!(x, [7, 2]);
}

#[test]
fn test_operations() {
    let mut x = Dim([1, 2]);
    let mut y = Dim([1, 1]);

    assert_eq!(x + y, [2, 3]);

    x += y;
    assert_eq!(x, [2, 3]);
    x *= 2;
    assert_eq!(x, [4, 6]);

    y[0] -= 1;
    assert_eq!(y, [0, 1]);
}

#[test]
fn test_generic_operations() {
    fn test_dim<D: Dimension>(d: &D) {
        let mut x = d.clone();
        x[0] += 1;
        assert_eq!(x[0], 3);
        x += d;
        assert_eq!(x[0], 5);
    }

    test_dim(&Dim([2, 3, 4]));
    test_dim(&Dim(vec![2, 3, 4, 1]));
    test_dim(&Dim(2));
}

#[test]
fn test_array_view() {
    fn test_dim<D: Dimension>(d: &D) {
        assert_eq!(d.as_array_view().scalar_sum(), 7);
        assert_eq!(d.as_array_view().strides(), &[1]);
    }

    test_dim(&Dim([1, 2, 4]));
    test_dim(&Dim(vec![1, 1, 2, 3]));
    test_dim(&Dim(7));
}
<|endoftext|>


<commit_before>#![feature(try_trait_v2)]
#![feature(let_chains)]

mod builtin_functions;
mod compiler;
mod database;
mod discover;
mod incremental;
mod input;
mod language_server;
mod vm;

use crate::{
    compiler::{
        ast_to_hir::AstToHir, cst::CstDb, cst_to_ast::CstToAst, hir, hir_to_lir::HirToLir,
        rcst_to_cst::RcstToCst, string_to_rcst::StringToRcst,
    },
    database::{Database, PROJECT_DIRECTORY},
    input::Input,
    language_server::utils::LspPositionConversion,
    vm::{Status, Vm},
};
use compiler::lir::Lir;
use itertools::Itertools;
use language_server::CandyLanguageServer;
use log::{debug, error, info, LevelFilter};
use lspower::{LspService, Server};
use notify::{watcher, RecursiveMode, Watcher};
use std::{
    env::current_dir,
    fs,
    path::PathBuf,
    sync::{mpsc::channel, Arc},
    time::Duration,
};
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    raw_build(&options.file, options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(&options.file, options.debug);
                }
                Err(e) => error!("watch error: {:#?}", e),
            }
        }
    }
}
fn raw_build(file: &PathBuf, debug: bool) -> Option<Arc<Lir>> {
    let path_string = file.to_string_lossy();
    debug!("Building `{}`.", path_string);

    let input: Input = file.clone().into();
    let db = Database::default();

    info!("Parsing string to RCST…");
    let rcst = db
        .rcst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.rcst");
        fs::write(hir_file, format!("{:#?}\n", rcst.clone())).unwrap();
    }

    info!("Turning RCST to CST…");
    let cst = db
        .cst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let cst_file = file.clone_with_extension("candy.cst");
        fs::write(cst_file, format!("{:#?}\n", cst.clone())).unwrap();
    }

    info!("Abstracting CST to AST…");
    let (asts, ast_cst_id_map) = db
        .ast(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let ast_file = file.clone_with_extension("candy.ast");
        fs::write(
            ast_file,
            format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
        )
        .unwrap();

        let ast_to_cst_ids_file = file.clone_with_extension("candy.ast_to_cst_ids");
        fs::write(
            ast_to_cst_ids_file,
            ast_cst_id_map
                .keys()
                .into_iter()
                .sorted_by_key(|it| it.local)
                .map(|key| format!("{} -> {}\n", key, ast_cst_id_map[key].0))
                .join(""),
        )
        .unwrap();
    }

    info!("Turning AST to HIR…");
    let (hir, hir_ast_id_map) = db
        .hir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.hir");
        fs::write(hir_file, format!("{}", hir.clone())).unwrap();

        let hir_ast_id_file = file.clone_with_extension("candy.hir_to_ast_ids");
        fs::write(
            hir_ast_id_file,
            hir_ast_id_map
                .keys()
                .into_iter()
                .map(|key| format!("{} -> {}\n", key, hir_ast_id_map[key]))
                .join(""),
        )
        .unwrap();
    }

    info!("Lowering HIR to LIR…");
    let lir = db
        .lir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let lir_file = file.clone_with_extension("candy.lir");
        fs::write(lir_file, format!("{}", lir)).unwrap();
    }

    Some(lir)
}

fn run(options: CandyRunOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    debug!("Running `{}`.\n", options.file.display());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    let lir = match raw_build(&options.file, false) {
        Some(lir) => lir,
        None => {
            log::info!("Build failed.");
            return;
        }
    };

    let path_string = options.file.to_string_lossy();
    debug!("Running `{}`.", path_string);

    let mut vm = Vm::new(lir.chunks.clone());
    vm.run(1000);
    match vm.status() {
        Status::Running => info!("VM is still running."),
        Status::Done(value) => info!("VM is done: {}", value),
        Status::Panicked(value) => {
            error!("VM panicked: {:#?}", value);

            error!("Stack trace:");
            let (_, hir_to_ast_ids) = db.hir(input.clone()).unwrap();
            let (_, ast_to_cst_ids) = db.ast(input.clone()).unwrap();
            for entry in vm.current_stack_trace().into_iter().rev() {
                let hir_id = entry.id;
                let ast_id = hir_to_ast_ids[&hir_id].clone();
                let cst_id = ast_to_cst_ids[&ast_id];
                let cst = db.find_cst(input.clone(), cst_id);
                let start = db.offset_to_lsp(input.clone(), cst.span.start);
                let end = db.offset_to_lsp(input.clone(), cst.span.end);
                error!(
                    "{}, {}, {:?}, {}:{} – {}:{}",
                    hir_id, ast_id, cst_id, start.0, start.1, end.0, end.1
                );
            }
        }
    }
}

async fn lsp() {
    info!("Starting language server…");
    let (service, messages) = LspService::new(|client| CandyLanguageServer::from_client(client));
    Server::new(tokio::io::stdin(), tokio::io::stdout())
        .interleave(messages)
        .serve(service)
        .await;
}

fn init_logger() {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{} [{:>5}] {} {}",
                chrono::Local::now().format("%H:%M:%S"),
                record.level(),
                record.target(),
                message
            ))
        })
        .level_for("salsa", LevelFilter::Error)
        .level_for("tokio_util", LevelFilter::Error)
        .level_for("lspower::transport", LevelFilter::Error)
        .level_for("candy::compiler::string_to_rcst", LevelFilter::Debug)
        .chain(std::io::stderr())
        .apply()
        .unwrap();
}

trait CloneWithExtension {
    fn clone_with_extension(&self, extension: &'static str) -> Self;
}
impl CloneWithExtension for PathBuf {
    fn clone_with_extension(&self, extension: &'static str) -> Self {
        let mut path = self.clone();
        assert!(path.set_extension(extension));
        path
    }
}
<commit_msg>Use saner logging defaults<commit_after>#![feature(try_trait_v2)]
#![feature(let_chains)]

mod builtin_functions;
mod compiler;
mod database;
mod discover;
mod incremental;
mod input;
mod language_server;
mod vm;

use crate::{
    compiler::{
        ast_to_hir::AstToHir, cst::CstDb, cst_to_ast::CstToAst, hir, hir_to_lir::HirToLir,
        rcst_to_cst::RcstToCst, string_to_rcst::StringToRcst,
    },
    database::{Database, PROJECT_DIRECTORY},
    input::Input,
    language_server::utils::LspPositionConversion,
    vm::{Status, Vm},
};
use compiler::lir::Lir;
use itertools::Itertools;
use language_server::CandyLanguageServer;
use log::{debug, error, info, LevelFilter};
use lspower::{LspService, Server};
use notify::{watcher, RecursiveMode, Watcher};
use std::{
    env::current_dir,
    fs,
    path::PathBuf,
    sync::{mpsc::channel, Arc},
    time::Duration,
};
use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "candy", about = "The 🍭 Candy CLI.")]
enum CandyOptions {
    Build(CandyBuildOptions),
    Run(CandyRunOptions),
    Lsp,
}

#[derive(StructOpt, Debug)]
struct CandyBuildOptions {
    #[structopt(long)]
    debug: bool,

    #[structopt(long)]
    watch: bool,

    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[derive(StructOpt, Debug)]
struct CandyRunOptions {
    #[structopt(parse(from_os_str))]
    file: PathBuf,
}

#[tokio::main]
async fn main() {
    init_logger();
    match CandyOptions::from_args() {
        CandyOptions::Build(options) => build(options),
        CandyOptions::Run(options) => run(options),
        CandyOptions::Lsp => lsp().await,
    }
}

fn build(options: CandyBuildOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    raw_build(&options.file, options.debug);

    if options.watch {
        let (tx, rx) = channel();
        let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
        watcher
            .watch(&options.file, RecursiveMode::Recursive)
            .unwrap();
        loop {
            match rx.recv() {
                Ok(_) => {
                    raw_build(&options.file, options.debug);
                }
                Err(e) => error!("watch error: {:#?}", e),
            }
        }
    }
}
fn raw_build(file: &PathBuf, debug: bool) -> Option<Arc<Lir>> {
    let path_string = file.to_string_lossy();
    debug!("Building `{}`.", path_string);

    let input: Input = file.clone().into();
    let db = Database::default();

    info!("Parsing string to RCST…");
    let rcst = db
        .rcst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.rcst");
        fs::write(hir_file, format!("{:#?}\n", rcst.clone())).unwrap();
    }

    info!("Turning RCST to CST…");
    let cst = db
        .cst(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let cst_file = file.clone_with_extension("candy.cst");
        fs::write(cst_file, format!("{:#?}\n", cst.clone())).unwrap();
    }

    info!("Abstracting CST to AST…");
    let (asts, ast_cst_id_map) = db
        .ast(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let ast_file = file.clone_with_extension("candy.ast");
        fs::write(
            ast_file,
            format!("{}\n", asts.iter().map(|ast| format!("{}", ast)).join("\n")),
        )
        .unwrap();

        let ast_to_cst_ids_file = file.clone_with_extension("candy.ast_to_cst_ids");
        fs::write(
            ast_to_cst_ids_file,
            ast_cst_id_map
                .keys()
                .into_iter()
                .sorted_by_key(|it| it.local)
                .map(|key| format!("{} -> {}\n", key, ast_cst_id_map[key].0))
                .join(""),
        )
        .unwrap();
    }

    info!("Turning AST to HIR…");
    let (hir, hir_ast_id_map) = db
        .hir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let hir_file = file.clone_with_extension("candy.hir");
        fs::write(hir_file, format!("{}", hir.clone())).unwrap();

        let hir_ast_id_file = file.clone_with_extension("candy.hir_to_ast_ids");
        fs::write(
            hir_ast_id_file,
            hir_ast_id_map
                .keys()
                .into_iter()
                .map(|key| format!("{} -> {}\n", key, hir_ast_id_map[key]))
                .join(""),
        )
        .unwrap();
    }

    info!("Lowering HIR to LIR…");
    let lir = db
        .lir(input.clone())
        .unwrap_or_else(|| panic!("File `{}` not found.", path_string));
    if debug {
        let lir_file = file.clone_with_extension("candy.lir");
        fs::write(lir_file, format!("{}", lir)).unwrap();
    }

    Some(lir)
}

fn run(options: CandyRunOptions) {
    *PROJECT_DIRECTORY.lock().unwrap() = Some(current_dir().unwrap());

    debug!("Running `{}`.\n", options.file.display());

    let input: Input = options.file.clone().into();
    let db = Database::default();

    let lir = match raw_build(&options.file, false) {
        Some(lir) => lir,
        None => {
            log::info!("Build failed.");
            return;
        }
    };

    let path_string = options.file.to_string_lossy();
    debug!("Running `{}`.", path_string);

    let mut vm = Vm::new(lir.chunks.clone());
    vm.run(1000);
    match vm.status() {
        Status::Running => info!("VM is still running."),
        Status::Done(value) => info!("VM is done: {}", value),
        Status::Panicked(value) => {
            error!("VM panicked: {:#?}", value);

            error!("Stack trace:");
            let (_, hir_to_ast_ids) = db.hir(input.clone()).unwrap();
            let (_, ast_to_cst_ids) = db.ast(input.clone()).unwrap();
            for entry in vm.current_stack_trace().into_iter().rev() {
                let hir_id = entry.id;
                let ast_id = hir_to_ast_ids[&hir_id].clone();
                let cst_id = ast_to_cst_ids[&ast_id];
                let cst = db.find_cst(input.clone(), cst_id);
                let start = db.offset_to_lsp(input.clone(), cst.span.start);
                let end = db.offset_to_lsp(input.clone(), cst.span.end);
                error!(
                    "{}, {}, {:?}, {}:{} – {}:{}",
                    hir_id, ast_id, cst_id, start.0, start.1, end.0, end.1
                );
            }
        }
    }
}

async fn lsp() {
    info!("Starting language server…");
    let (service, messages) = LspService::new(|client| CandyLanguageServer::from_client(client));
    Server::new(tokio::io::stdin(), tokio::io::stdout())
        .interleave(messages)
        .serve(service)
        .await;
}

fn init_logger() {
    fern::Dispatch::new()
        .format(|out, message, record| {
            out.finish(format_args!(
                "{} [{:>5}] {} {}",
                chrono::Local::now().format("%H:%M:%S"),
                record.level(),
                record.target(),
                message
            ))
        })
        .level_for("candy::compiler::string_to_rcst", LevelFilter::Debug)
        .level_for("candy::vm::builtin_functions", LevelFilter::Warn)
        .level_for("candy::vm::heap", LevelFilter::Debug)
        .level_for("candy::vm::vm", LevelFilter::Debug)
        .level_for("lspower::transport", LevelFilter::Error)
        .level_for("salsa", LevelFilter::Error)
        .level_for("tokio_util", LevelFilter::Error)
        .chain(std::io::stderr())
        .apply()
        .unwrap();
}

trait CloneWithExtension {
    fn clone_with_extension(&self, extension: &'static str) -> Self;
}
impl CloneWithExtension for PathBuf {
    fn clone_with_extension(&self, extension: &'static str) -> Self {
        let mut path = self.clone();
        assert!(path.set_extension(extension));
        path
    }
}
<|endoftext|>


<commit_before>use std::result;

use reqwest;
use serde::Deserialize;

use errors::*;

#[derive(Clone, Debug)]
pub struct Location {
    pub description: String,
    pub coord: Coordinate,
}

#[derive(Debug, Deserialize)]
struct IPInfo {
    #[serde(rename = "loc")] coord: Coordinate,
    city: String,
    region: String,
}

impl Location {
    pub fn from_ip() -> Result<Self> {
        let ip_info: IPInfo = reqwest::get("https://ipinfo.io/json")?.json()?;
        let description = format!("{}, {}", ip_info.city, ip_info.region);
        let coord = ip_info.coord;
        Ok(Self { description, coord })
    }
}

#[derive(Clone, Debug)]
pub struct Coordinate(pub f64, pub f64);

impl<'de> Deserialize<'de> for Coordinate {
    fn deserialize<D>(deserializer: D) -> result::Result<Coordinate, D::Error>
    where
        D: ::serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        let mut split = s.split(',');
        let lat = split
            .next()
            .and_then(|lat| lat.parse().ok())
            .ok_or_else(|| ::serde::de::Error::custom(""))?;
        let long = split
            .next()
            .and_then(|long| long.parse().ok())
            .ok_or_else(|| ::serde::de::Error::custom(""))?;
        Ok(Coordinate(lat, long))
    }
}

#[test]
fn test_deserializing_coordinate() {
    let c: Result<Coordinate, _> = ::serde_json::from_str("\"\"");
    assert!(c.is_err());

    let c: Result<Coordinate, _> = ::serde_json::from_str("\"123\"");
    assert!(c.is_err());

    let c: Result<Coordinate, _> = ::serde_json::from_str("\"123,\"");
    assert!(c.is_err());

    let Coordinate(lat, long) = ::serde_json::from_str("\"123,-123\"").unwrap();
    assert_eq!(lat, 123.0);
    assert_eq!(long, -123.0);
}
<commit_msg>Fix tests<commit_after>use std::result;

use reqwest;
use serde::Deserialize;

use errors::*;

#[derive(Clone, Debug)]
pub struct Location {
    pub description: String,
    pub coord: Coordinate,
}

#[derive(Debug, Deserialize)]
struct IPInfo {
    #[serde(rename = "loc")] coord: Coordinate,
    city: String,
    region: String,
}

impl Location {
    pub fn from_ip() -> Result<Self> {
        let ip_info: IPInfo = reqwest::get("https://ipinfo.io/json")?.json()?;
        let description = format!("{}, {}", ip_info.city, ip_info.region);
        let coord = ip_info.coord;
        Ok(Self { description, coord })
    }
}

#[derive(Clone, Debug)]
pub struct Coordinate(pub f64, pub f64);

impl<'de> Deserialize<'de> for Coordinate {
    fn deserialize<D>(deserializer: D) -> result::Result<Coordinate, D::Error>
    where
        D: ::serde::Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        let mut split = s.split(',');
        let lat = split
            .next()
            .and_then(|lat| lat.parse().ok())
            .ok_or_else(|| ::serde::de::Error::custom(""))?;
        let long = split
            .next()
            .and_then(|long| long.parse().ok())
            .ok_or_else(|| ::serde::de::Error::custom(""))?;
        Ok(Coordinate(lat, long))
    }
}

#[test]
fn test_deserializing_coordinate() {
    let c: result::Result<Coordinate, _> = ::serde_json::from_str("\"\"");
    assert!(c.is_err());

    let c: result::Result<Coordinate, _> = ::serde_json::from_str("\"123\"");
    assert!(c.is_err());

    let c: result::Result<Coordinate, _> = ::serde_json::from_str("\"123,\"");
    assert!(c.is_err());

    let Coordinate(lat, long) = ::serde_json::from_str("\"123,-123\"").unwrap();
    assert_eq!(lat, 123.0);
    assert_eq!(long, -123.0);
}
<|endoftext|>


<commit_before>use std::mem::size_of;

use lfsr::*;

#[derive(PartialEq, Eq, Clone, Hash, Debug)]
pub struct GaloisLFSR {
    pub width: usize,
    pub taps: Vec<usize>,
    tapmasks: Vec<usize>,
    pub state: Vec<usize>,
}

impl GaloisLFSR {
    // Warning: compared to the Fibonacci-style LFSRs this takes bit numbers the other way around.
    // For compatibility with such, use Self::fibonacci().
    #[allow(dead_code)]
    pub fn new(width: usize, taps: Vec<usize>, seed: Vec<usize>) -> Result<GaloisLFSR, String> {
        if width > size_of::<usize>() * 8 {
            return Err(format!("Unsupported: Width {} exceeded that of a single usize.",
                               width));
        }
        let tapmasks = calculate_tapmasks(width, taps.clone())?;
        Ok(GaloisLFSR {
            width: width,
            taps: taps,
            tapmasks: tapmasks,
            state: seed,
        })
    }

    // Instantiate a GaloisLFSR as if it were a Fibonacci-style LFSR. Essentially this method
    // will give a GaloisLFSR whose output will be identical to that from the Fibonacci-style
    // LFSR implementations.
    #[allow(dead_code)]
    pub fn fibonacci(width: usize,
                     taps: Vec<usize>,
                     seed: Vec<usize>)
                     -> Result<GaloisLFSR, String> {
        let mirrored_taps = Self::mirror_taps(width, taps)?;
        Self::new(width, mirrored_taps, seed)
    }

    fn mirror_taps(width: usize, taps: Vec<usize>) -> Result<Vec<usize>, String> {
        if taps.iter().max().unwrap_or(&1) > &width {
            return Err("Tap was outside of register width.".to_string());
        }
        Ok(taps.into_iter().map(|tap| width - tap + 1).collect())
    }
}

impl LFSRTrait for GaloisLFSR {
    fn clock(&mut self) -> usize {
        let output_bit = self.state[0] & 1;
        self.state[0] >>= 1;
        // https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs suggested this
        // routine, which I tweaked to suit Rust typing.
        self.state[0] ^= (-(output_bit as isize) & self.tapmasks[0] as isize) as usize;
        output_bit
    }

    fn multiclock(&mut self, clocks: usize) -> Vec<usize> {
        let mut outputs = Vec::with_capacity(clocks);
        for _ in 0..clocks {
            outputs.push(self.clock());
        }
        return outputs;
    }

    fn get(&self) -> Vec<usize> {
        self.state.clone()
    }

    fn set(&mut self, value: Vec<usize>) {
        self.state = value;
    }

    fn taps(&self) -> Vec<usize> {
        self.taps.clone()
    }

    fn width(&self) -> usize {
        self.width
    }
}

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

    #[test]
    fn ticks_as_expected() {
        let mut naive_lfsr = NaiveLFSR::new(7, vec![1, 2], vec![44]).unwrap();
        let mut galois_lfsr = GaloisLFSR::fibonacci(7, vec![1, 2], vec![44]).unwrap();

        for i in 0..32768 {
            println!("{}", i);
            assert!(naive_lfsr.clock() == galois_lfsr.clock());
        }
    }

    #[test]
    fn rejects_width_larger_than_usize() {
        let usize_bytes = size_of::<usize>();
        let usize_bits = usize_bytes * 8;

        for width in (usize_bits + 1)..(usize_bits * 2 + 1) {
            assert!(GaloisLFSR::fibonacci(width, vec![1], vec![0]).is_err())
        }
    }

    #[test]
    fn rejects_tap_larger_than_register() {
        for width in 0..64 {
            for tap in (width + 1)..64 {
                assert!(GaloisLFSR::fibonacci(width, vec![tap], vec![0]).is_err())
            }
        }
    }
}
<commit_msg>Resolved worse GaloisLFSR performance. #4<commit_after>use std::mem::size_of;

use lfsr::*;

#[derive(PartialEq, Eq, Clone, Hash, Debug)]
pub struct GaloisLFSR {
    pub width: usize,
    pub taps: Vec<usize>,
    tapmasks: Vec<usize>,
    pub state: Vec<usize>,
}

impl GaloisLFSR {
    // Warning: compared to the Fibonacci-style LFSRs this takes bit numbers the other way around.
    // For compatibility with such, use Self::fibonacci().
    #[allow(dead_code)]
    pub fn new(width: usize, taps: Vec<usize>, seed: Vec<usize>) -> Result<GaloisLFSR, String> {
        if width > size_of::<usize>() * 8 {
            return Err(format!("Unsupported: Width {} exceeded that of a single usize.",
                               width));
        }
        let tapmasks = calculate_tapmasks(width, taps.clone())?;
        Ok(GaloisLFSR {
            width: width,
            taps: taps,
            tapmasks: tapmasks,
            state: seed,
        })
    }

    // Instantiate a GaloisLFSR as if it were a Fibonacci-style LFSR. Essentially this method
    // will give a GaloisLFSR whose output will be identical to that from the Fibonacci-style
    // LFSR implementations.
    #[allow(dead_code)]
    pub fn fibonacci(width: usize,
                     taps: Vec<usize>,
                     seed: Vec<usize>)
                     -> Result<GaloisLFSR, String> {
        let mirrored_taps = Self::mirror_taps(width, taps)?;
        Self::new(width, mirrored_taps, seed)
    }

    fn mirror_taps(width: usize, taps: Vec<usize>) -> Result<Vec<usize>, String> {
        if taps.iter().max().unwrap_or(&1) > &width {
            return Err("Tap was outside of register width.".to_string());
        }
        Ok(taps.into_iter().map(|tap| width - tap + 1).collect())
    }

    fn clock_word(mut word: usize, tapmask: usize) -> (usize, usize) {
        let output_bit = word & 1;
        word >>= 1;
        // https://en.wikipedia.org/wiki/Linear-feedback_shift_register#Galois_LFSRs suggested this
        // routine, which I tweaked to suit Rust typing.
        word ^= (-(output_bit as isize) & tapmask as isize) as usize;
        (word, output_bit)
    }
}

impl LFSRTrait for GaloisLFSR {
    fn clock(&mut self) -> usize {
        let (word, output_bit) = Self::clock_word(self.state[0], self.tapmasks[0]);
        self.state[0] = word;
        output_bit
    }

    fn multiclock(&mut self, clocks: usize) -> Vec<usize> {
        let mut outputs = Vec::with_capacity(clocks);
        for _ in 0..clocks {
            outputs.push(self.clock());
        }
        return outputs;
    }

    fn get(&self) -> Vec<usize> {
        self.state.clone()
    }

    fn set(&mut self, value: Vec<usize>) {
        self.state = value;
    }

    fn taps(&self) -> Vec<usize> {
        self.taps.clone()
    }

    fn width(&self) -> usize {
        self.width
    }
}

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

    #[test]
    fn ticks_as_expected() {
        let mut naive_lfsr = NaiveLFSR::new(7, vec![1, 2], vec![44]).unwrap();
        let mut galois_lfsr = GaloisLFSR::fibonacci(7, vec![1, 2], vec![44]).unwrap();

        for i in 0..32768 {
            println!("{}", i);
            assert!(naive_lfsr.clock() == galois_lfsr.clock());
        }
    }

    #[test]
    fn rejects_width_larger_than_usize() {
        let usize_bytes = size_of::<usize>();
        let usize_bits = usize_bytes * 8;

        for width in (usize_bits + 1)..(usize_bits * 2 + 1) {
            assert!(GaloisLFSR::fibonacci(width, vec![1], vec![0]).is_err())
        }
    }

    #[test]
    fn rejects_tap_larger_than_register() {
        for width in 0..64 {
            for tap in (width + 1)..64 {
                assert!(GaloisLFSR::fibonacci(width, vec![tap], vec![0]).is_err())
            }
        }
    }
}
<|endoftext|>


<commit_before>import std::ivec;
import std::uint;
import std::str;
import std::termivec;
import std::ioivec;
import std::option;
import std::option::some;
import std::option::none;

type filename = str;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */
type filemap = @rec(filename name, uint start_pos, mutable uint[] lines);

type codemap = @rec(mutable filemap[] files);

type loc = rec(filename filename, uint line, uint col);

fn new_codemap() -> codemap {
    let filemap[] files = ~[];
    ret @rec(mutable files=files);
}

fn new_filemap(filename filename, uint start_pos) -> filemap {
    ret @rec(name=filename, start_pos=start_pos, mutable lines=~[0u]);
}

fn next_line(filemap file, uint pos) { file.lines += ~[pos]; }

fn lookup_pos(codemap map, uint pos) -> loc {
    auto a = 0u;
    auto b = ivec::len[filemap](map.files);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
    }
    auto f = map.files.(a);
    a = 0u;
    b = ivec::len[uint](f.lines);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (f.lines.(m) > pos) { b = m; } else { a = m; }
    }
    ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
}

type span = rec(uint lo, uint hi);

fn span_to_str(&span sp, &codemap cm) -> str {
    auto lo = lookup_pos(cm, sp.lo);
    auto hi = lookup_pos(cm, sp.hi);
    ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
}

fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
                   &codemap cm) {
    auto ss = "<input>:0:0:0:0";
    let option::t[@file_lines] maybe_lines = none;
    alt (sp) {
        case (some(?ssp)) {
            ss = span_to_str(ssp, cm);

            // FIXME: we're not able to look up lines read from .rc files yet.
            // maybe_lines = some(span_to_lines(ssp, cm));

        }
        case (none) { }
    }
    ioivec::stdout().write_str(ss + ": ");
    if (termivec::color_supported()) {
        termivec::fg(ioivec::stdout().get_buf_writer(), color);
    }
    ioivec::stdout().write_str(#fmt("%s:", kind));
    if (termivec::color_supported()) {
        termivec::reset(ioivec::stdout().get_buf_writer());
    }
    ioivec::stdout().write_str(#fmt(" %s\n", msg));
    alt (maybe_lines) {
        case (some(?lines)) {
            // FIXME: reading in the entire file is the worst possible way to
            //        get access to the necessary lines.
            auto rdr = ioivec::file_reader(lines.name);
            auto file = str::unsafe_from_bytes_ivec(rdr.read_whole_stream());
            auto fm = codemap::get_filemap(cm, lines.name);

            // arbitrarily only print up to six lines of the error
            auto max_lines = 6u;
            auto elided = false;
            auto display_lines = lines.lines;
            if (ivec::len(display_lines) > max_lines) {
                display_lines = ivec::slice(display_lines, 0u, max_lines);
                elided = true;
            }
            // Print the offending lines
            for (uint line in display_lines) {
                ioivec::stdout().write_str(#fmt("%s:%u ", fm.name,
                                                line + 1u));
                auto s = codemap::get_line(fm, line as int, file);
                if (!str::ends_with(s, "\n")) {
                    s += "\n";
                }
                ioivec::stdout().write_str(s);
            }
            if (elided) {
                auto last_line = display_lines.(ivec::len(display_lines) -
                                                1u);
                auto s = #fmt("%s:%u ", fm.name, last_line + 1u);
                auto indent = str::char_len(s);
                auto out = "";
                while (indent > 0u) { out += " "; indent -= 1u; }
                out += "...\n";
                ioivec::stdout().write_str(out);
            }

            // If there's one line at fault we can easily point to the problem
            if (ivec::len(lines.lines) == 1u) {
                auto lo = codemap::lookup_pos(cm, option::get(sp).lo);
                auto digits = 0u;
                auto num = lines.lines.(0) / 10u;

                // how many digits must be indent past?
                while (num > 0u) { num /= 10u; digits += 1u; }

                // indent past |name:## | and the 0-offset column location
                auto left = str::char_len(fm.name) + digits + lo.col + 3u;
                auto s = "";
                while (left > 0u) { str::push_char(s, ' '); left -= 1u; }

                s += "^";
                auto hi = codemap::lookup_pos(cm, option::get(sp).hi);
                if (hi.col != lo.col) {
                    // the ^ already takes up one space
                    auto width = hi.col - lo.col - 1u;
                    while (width > 0u) {
                        str::push_char(s, '~');
                        width -= 1u;
                    }
                }
                ioivec::stdout().write_str(s + "\n");
            }
        }
        case (_) {}
    }
}

fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "warning", 11u8, cm);
}
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "error", 9u8, cm);
}
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "note", 10u8, cm);
}

type file_lines = rec(str name, uint[] lines);

fn span_to_lines(span sp, codemap::codemap cm) -> @file_lines {
    auto lo = codemap::lookup_pos(cm, sp.lo);
    auto hi = codemap::lookup_pos(cm, sp.hi);
    auto lines = ~[];
    for each (uint i in uint::range(lo.line - 1u, hi.line as uint)) {
        lines += ~[i];
    }
    ret @rec(name=lo.filename, lines=lines);
}

fn get_line(filemap fm, int line, &str file) -> str {
    let uint end;
    if ((line as uint) + 1u >= ivec::len(fm.lines)) {
        end = str::byte_len(file);
    } else {
        end = fm.lines.(line + 1);
    }
    ret str::slice(file, fm.lines.(line), end);
}

fn get_filemap(codemap cm, str filename) -> filemap {
    for (filemap fm in cm.files) {
        if (fm.name == filename) {
            ret fm;
        }
    }
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
    fail;// ("asking for " + filename + " which we don't know about");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Fix error line display slicing.<commit_after>import std::ivec;
import std::uint;
import std::str;
import std::termivec;
import std::ioivec;
import std::option;
import std::option::some;
import std::option::none;

type filename = str;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */
type filemap = @rec(filename name, uint start_pos, mutable uint[] lines);

type codemap = @rec(mutable filemap[] files);

type loc = rec(filename filename, uint line, uint col);

fn new_codemap() -> codemap {
    let filemap[] files = ~[];
    ret @rec(mutable files=files);
}

fn new_filemap(filename filename, uint start_pos) -> filemap {
    ret @rec(name=filename, start_pos=start_pos, mutable lines=~[0u]);
}

fn next_line(filemap file, uint pos) { file.lines += ~[pos]; }

fn lookup_pos(codemap map, uint pos) -> loc {
    auto a = 0u;
    auto b = ivec::len[filemap](map.files);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
    }
    auto f = map.files.(a);
    a = 0u;
    b = ivec::len[uint](f.lines);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (f.lines.(m) > pos) { b = m; } else { a = m; }
    }
    ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
}

type span = rec(uint lo, uint hi);

fn span_to_str(&span sp, &codemap cm) -> str {
    auto lo = lookup_pos(cm, sp.lo);
    auto hi = lookup_pos(cm, sp.hi);
    ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
}

fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
                   &codemap cm) {
    auto ss = "<input>:0:0:0:0";
    let option::t[@file_lines] maybe_lines = none;
    alt (sp) {
        case (some(?ssp)) {
            ss = span_to_str(ssp, cm);

            // FIXME: we're not able to look up lines read from .rc files yet.
            // maybe_lines = some(span_to_lines(ssp, cm));

        }
        case (none) { }
    }
    ioivec::stdout().write_str(ss + ": ");
    if (termivec::color_supported()) {
        termivec::fg(ioivec::stdout().get_buf_writer(), color);
    }
    ioivec::stdout().write_str(#fmt("%s:", kind));
    if (termivec::color_supported()) {
        termivec::reset(ioivec::stdout().get_buf_writer());
    }
    ioivec::stdout().write_str(#fmt(" %s\n", msg));
    alt (maybe_lines) {
        case (some(?lines)) {
            // FIXME: reading in the entire file is the worst possible way to
            //        get access to the necessary lines.
            auto rdr = ioivec::file_reader(lines.name);
            auto file = str::unsafe_from_bytes_ivec(rdr.read_whole_stream());
            auto fm = get_filemap(cm, lines.name);

            // arbitrarily only print up to six lines of the error
            auto max_lines = 6u;
            auto elided = false;
            auto display_lines = lines.lines;
            if (ivec::len(display_lines) > max_lines) {
                display_lines = ivec::slice(display_lines, 0u, max_lines);
                elided = true;
            }
            // Print the offending lines
            for (uint line in display_lines) {
                ioivec::stdout().write_str(#fmt("%s:%u ", fm.name,
                                                line + 1u));
                auto s = get_line(fm, line as int, file);
                if (!str::ends_with(s, "\n")) {
                    s += "\n";
                }
                ioivec::stdout().write_str(s);
            }
            if (elided) {
                auto last_line = display_lines.(ivec::len(display_lines) -
                                                1u);
                auto s = #fmt("%s:%u ", fm.name, last_line + 1u);
                auto indent = str::char_len(s);
                auto out = "";
                while (indent > 0u) { out += " "; indent -= 1u; }
                out += "...\n";
                ioivec::stdout().write_str(out);
            }

            // If there's one line at fault we can easily point to the problem
            if (ivec::len(lines.lines) == 1u) {
                auto lo = codemap::lookup_pos(cm, option::get(sp).lo);
                auto lo = lookup_pos(cm, option::get(sp).lo);
                auto digits = 0u;
                auto num = lines.lines.(0) / 10u;

                // how many digits must be indent past?
                while (num > 0u) { num /= 10u; digits += 1u; }

                // indent past |name:## | and the 0-offset column location
                auto left = str::char_len(fm.name) + digits + lo.col + 3u;
                auto s = "";
                while (left > 0u) { str::push_char(s, ' '); left -= 1u; }

                s += "^";
                auto hi = lookup_pos(cm, option::get(sp).hi);
                if (hi.col != lo.col) {
                    // the ^ already takes up one space
                    auto width = hi.col - lo.col - 1u;
                    while (width > 0u) {
                        str::push_char(s, '~');
                        width -= 1u;
                    }
                }
                ioivec::stdout().write_str(s + "\n");
            }
        }
        case (_) {}
    }
}

fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "warning", 11u8, cm);
}
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "error", 9u8, cm);
}
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "note", 10u8, cm);
}

type file_lines = rec(str name, uint[] lines);

fn span_to_lines(span sp, codemap::codemap cm) -> @file_lines {
    auto lo = codemap::lookup_pos(cm, sp.lo);
    auto hi = codemap::lookup_pos(cm, sp.hi);
    auto lines = ~[];
    for each (uint i in uint::range(lo.line - 1u, hi.line as uint)) {
        lines += ~[i];
    }
    ret @rec(name=lo.filename, lines=lines);
}

fn get_line(filemap fm, int line, &str file) -> str {
    let uint begin = fm.lines.(line) - fm.lines.(0);
    let uint end;
    if ((line as uint) + 1u >= ivec::len(fm.lines)) {
        end = str::byte_len(file);
    } else {
        end = fm.lines.(line + 1) - fm.lines.(0);
    }
    ret str::slice(file, begin, end);
}

fn get_filemap(codemap cm, str filename) -> filemap {
    for (filemap fm in cm.files) {
        if (fm.name == filename) {
            ret fm;
        }
    }
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
    fail;// ("asking for " + filename + " which we don't know about");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>