<commit_before>pub mod bldr;
pub mod cli;
pub mod config;
pub mod file;
pub mod license;
pub mod origin;
pub mod pkg;
pub mod plan;
pub mod ring;
pub mod studio;
pub mod sup;
pub mod svc;
#[cfg(test)]
mod tests;
pub mod user;
pub mod util;

use self::{bldr::{ConfigOptChannelCreate,
                  ConfigOptChannelDemote,
                  ConfigOptChannelDestroy,
                  ConfigOptChannelList,
                  ConfigOptChannelPromote,
                  ConfigOptJobCancel,
                  ConfigOptJobDemote,
                  ConfigOptJobPromote,
                  ConfigOptJobStart,
                  ConfigOptJobStatus,
                  ChannelCreate,
                  ChannelDemote,
                  ChannelDestroy,
                  ChannelList,
                  ChannelPromote,
                  JobCancel,
                  JobDemote,
                  JobPromote,
                  JobStart,
                  JobStatus},
           cli::{CliCompleters,
                 ConfigOptCliCompleters,
                 CliSetup,
                 ConfigOptCliSetup},
           config::{ConfigOptServiceConfigApply,
                    ConfigOptServiceConfigShow,
                    ServiceConfigApply,
                    ServiceConfigShow},
           file::{ConfigOptFileUpload,
                  FileUpload},
           license::{ConfigOptLicense,
                     License},
           origin::{ConfigOptOrigin,
                    Origin},
           pkg::{ConfigOptPkg,
                 ConfigOptPkgInstall,
                 Pkg,
                 PkgInstall},
           plan::{ConfigOptPlan,
                  Plan},
           ring::{ConfigOptRingKeyExport,
                  ConfigOptRingKeyImport,
                  ConfigOptRingKeyGenerate,
                  RingKeyExport,
                  RingKeyImport,
                  RingKeyGenerate},
           studio::{ConfigOptStudio,
                    Studio},
           sup::{ConfigOptHabSup,
                 ConfigOptSupRun,
                 HabSup,
                 SupRun},
           svc::{ConfigOptSvc,
                 ConfigOptSvcStart,
                 ConfigOptSvcStop,
                 Svc,
                 SvcStart,
                 SvcStop},
           user::{ConfigOptUserKeyGenerate,
                  UserKeyGenerate},
           util::{CacheKeyPath,
                  ConfigOptCacheKeyPath}};
use crate::{cli::AFTER_HELP,
            VERSION};
use configopt::ConfigOpt;
use structopt::{clap::AppSettings,
                StructOpt};

#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "hab",
            version = VERSION,
            about = "Patents: https://chef.io/patents\n\"A Habitat is the natural environment for your services\" - Alan Turing",
            author = "\nThe Habitat Maintainers <humans@habitat.sh>\n",
            settings = &[AppSettings::GlobalVersion],
            after_help = AFTER_HELP
        )]
#[allow(clippy::large_enum_variant)]
pub enum Hab {
    #[structopt(no_version)]
    Bldr(Bldr),
    #[structopt(no_version)]
    Cli(Cli),
    #[structopt(no_version)]
    Config(ServiceConfig),
    #[structopt(no_version)]
    File(File),
    #[structopt(no_version, settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
    License(License),
    #[structopt(no_version)]
    Origin(Origin),
    #[structopt(no_version)]
    Pkg(Pkg),
    #[structopt(no_version)]
    Plan(Plan),
    #[structopt(no_version)]
    Ring(Ring),
    #[structopt(no_version, aliases = &["stu", "stud", "studi"])]
    Studio(Studio),
    #[structopt(no_version)]
    Sup(HabSup),
    /// Create a tarball of Habitat Supervisor data to send to support
    #[structopt(no_version)]
    Supportbundle,
    #[structopt(no_version)]
    Svc(Svc),
    #[structopt(no_version)]
    User(User),

    /// Alias for 'config apply'
    #[structopt(no_version, settings = &[AppSettings::Hidden])]
    Apply(ServiceConfigApply),
    /// Alias for 'pkg install'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["i", "in", "ins", "inst", "insta", "instal"])]
    Install(PkgInstall),
    #[cfg(not(target_os = "macos"))]
    /// Alias for 'sup run'
    #[structopt(no_version, settings = &[AppSettings::Hidden])]
    Run(SupRun),
    /// Alias for 'cli setup'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["set", "setu"])]
    Setup(CacheKeyPath),
    /// Alias for 'svc start'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["sta", "star"])]
    Start(SvcStart),
    /// Alias for 'svc stop'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["sto"])]
    Stop(SvcStop),
    #[cfg(not(target_os = "macos"))]
    /// Alias for 'sup term'
    #[structopt(no_version, settings = &[AppSettings::Hidden])]
    Term,
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["b", "bl", "bld"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat Builder
pub enum Bldr {
    #[structopt(no_version)]
    Channel(Channel),
    #[structopt(no_version)]
    Job(Job),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["c", "ch", "cha", "chan", "chann", "channe"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat Builder channels
pub enum Channel {
    #[structopt(no_version, aliases = &["c", "cr", "cre", "crea", "creat"])]
    Create(ChannelCreate),
    Demote(ChannelDemote),
    #[structopt(no_version, aliases = &["d", "de", "des", "dest", "destr", "destro"])]
    Destroy(ChannelDestroy),
    #[structopt(no_version, aliases = &["l", "li", "lis"])]
    List(ChannelList),
    Promote(ChannelPromote),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["j", "jo"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat Builder jobs
pub enum Job {
    #[structopt(no_version, aliases = &["c", "ca", "can", "cance", "cancel"])]
    Cancel(JobCancel),
    #[structopt(no_version, aliases = &["d", "de", "dem", "demo", "demot"])]
    Demote(JobDemote),
    #[structopt(no_version, aliases = &["p", "pr", "pro", "prom", "promo", "promot"])]
    Promote(JobPromote),
    #[structopt(no_version, aliases = &["s", "st", "sta", "star"])]
    Start(JobStart),
    #[structopt(no_version, aliases = &["stat", "statu"])]
    Status(JobStatus),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["cl"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat runtime config
pub enum Cli {
    #[structopt(no_version, aliases = &["s", "se", "set", "setu"])]
    /// Sets up the CLI with reasonable defaults
    Setup(CliSetup),
    #[structopt(no_version, aliases = &["c", "co", "com", "comp"])]
    /// Creates command-line completers for your shell
    Completers(CliCompleters),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["co", "con", "conf", "confi"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to a Service's runtime config
pub enum ServiceConfig {
    #[structopt(no_version, aliases = &["ap", "app", "appl"])]
    Apply(ServiceConfigApply),
    #[structopt(no_version, aliases = &["sh", "sho"])]
    Show(ServiceConfigShow),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["f", "fi", "fil"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat files
pub enum File {
    #[structopt(no_version, aliases = &["u", "up", "upl", "uplo", "uploa"])]
    /// Uploads a file to be shared between members of a Service Group
    Upload(FileUpload),
}

/// Commands relating to Habitat users
#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "user", no_version, aliases = &["u", "us", "use"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
pub enum User {
    Key(UserKey),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "key", no_version, aliases = &["k", "ke"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat user keys
pub enum UserKey {
    #[structopt(no_version, aliases = &["g", "ge", "gen", "gene", "gener", "genera", "generat"])]
    Generate(UserKeyGenerate),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["r", "ri", "rin"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat rings
pub enum Ring {
    Key(RingKey),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "key", no_version, aliases = &["k", "ke"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat ring keys
pub enum RingKey {
    #[structopt(no_version, aliases = &["e", "ex", "exp", "expo", "expor"])]
    Export(RingKeyExport),
    #[structopt(no_version, aliases = &["i", "im", "imp", "impo", "impor"])]
    Import(RingKeyImport),
    #[structopt(no_version, aliases = &["g", "ge", "gen", "gene", "gener", "genera", "generat"])]
    Generate(RingKeyGenerate),
}
<commit_msg>[Refactor] added plan wrapper to hab<commit_after>pub mod bldr;
pub mod cli;
pub mod config;
pub mod file;
pub mod license;
pub mod origin;
pub mod pkg;
pub mod plan;
pub mod ring;
pub mod studio;
pub mod sup;
pub mod svc;
#[cfg(test)]
mod tests;
pub mod user;
pub mod util;

use self::{bldr::{ConfigOptChannelCreate,
                  ConfigOptChannelDemote,
                  ConfigOptChannelDestroy,
                  ConfigOptChannelList,
                  ConfigOptChannelPromote,
                  ConfigOptJobCancel,
                  ConfigOptJobDemote,
                  ConfigOptJobPromote,
                  ConfigOptJobStart,
                  ConfigOptJobStatus,
                  ChannelCreate,
                  ChannelDemote,
                  ChannelDestroy,
                  ChannelList,
                  ChannelPromote,
                  JobCancel,
                  JobDemote,
                  JobPromote,
                  JobStart,
                  JobStatus},
           cli::{CliCompleters,
                 ConfigOptCliCompleters,
                 CliSetup,
                 ConfigOptCliSetup},
           config::{ConfigOptServiceConfigApply,
                    ConfigOptServiceConfigShow,
                    ServiceConfigApply,
                    ServiceConfigShow},
           file::{ConfigOptFileUpload,
                  FileUpload},
           license::{ConfigOptLicense,
                     License},
           origin::{ConfigOptOrigin,
                    Origin},
           pkg::{ConfigOptPkg,
                 ConfigOptPkgInstall,
                 Pkg,
                 PkgInstall},
           plan::{ConfigOptPlanInit,
                  ConfigOptPlanRender,
                  PlanInit,
                  PlanRender},
           ring::{ConfigOptRingKeyExport,
                  ConfigOptRingKeyImport,
                  ConfigOptRingKeyGenerate,
                  RingKeyExport,
                  RingKeyImport,
                  RingKeyGenerate},
           studio::{ConfigOptStudio,
                    Studio},
           sup::{ConfigOptHabSup,
                 ConfigOptSupRun,
                 HabSup,
                 SupRun},
           svc::{ConfigOptSvc,
                 ConfigOptSvcStart,
                 ConfigOptSvcStop,
                 Svc,
                 SvcStart,
                 SvcStop},
           user::{ConfigOptUserKeyGenerate,
                  UserKeyGenerate},
           util::{CacheKeyPath,
                  ConfigOptCacheKeyPath}};
use crate::{cli::AFTER_HELP,
            VERSION};
use configopt::ConfigOpt;
use structopt::{clap::AppSettings,
                StructOpt};

#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "hab",
            version = VERSION,
            about = "Patents: https://chef.io/patents\n\"A Habitat is the natural environment for your services\" - Alan Turing",
            author = "\nThe Habitat Maintainers <humans@habitat.sh>\n",
            settings = &[AppSettings::GlobalVersion],
            after_help = AFTER_HELP
        )]
#[allow(clippy::large_enum_variant)]
pub enum Hab {
    #[structopt(no_version)]
    Bldr(Bldr),
    #[structopt(no_version)]
    Cli(Cli),
    #[structopt(no_version)]
    Config(ServiceConfig),
    #[structopt(no_version)]
    File(File),
    #[structopt(no_version, settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
    License(License),
    #[structopt(no_version)]
    Origin(Origin),
    #[structopt(no_version)]
    Pkg(Pkg),
    #[structopt(no_version)]
    Plan(Plan),
    #[structopt(no_version)]
    Ring(Ring),
    #[structopt(no_version, aliases = &["stu", "stud", "studi"])]
    Studio(Studio),
    #[structopt(no_version)]
    Sup(HabSup),
    /// Create a tarball of Habitat Supervisor data to send to support
    #[structopt(no_version)]
    Supportbundle,
    #[structopt(no_version)]
    Svc(Svc),
    #[structopt(no_version)]
    User(User),

    /// Alias for 'config apply'
    #[structopt(no_version, settings = &[AppSettings::Hidden])]
    Apply(ServiceConfigApply),
    /// Alias for 'pkg install'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["i", "in", "ins", "inst", "insta", "instal"])]
    Install(PkgInstall),
    #[cfg(not(target_os = "macos"))]
    /// Alias for 'sup run'
    #[structopt(no_version, settings = &[AppSettings::Hidden])]
    Run(SupRun),
    /// Alias for 'cli setup'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["set", "setu"])]
    Setup(CacheKeyPath),
    /// Alias for 'svc start'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["sta", "star"])]
    Start(SvcStart),
    /// Alias for 'svc stop'
    #[structopt(no_version, settings = &[AppSettings::Hidden], aliases = &["sto"])]
    Stop(SvcStop),
    #[cfg(not(target_os = "macos"))]
    /// Alias for 'sup term'
    #[structopt(no_version, settings = &[AppSettings::Hidden])]
    Term,
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["b", "bl", "bld"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat Builder
pub enum Bldr {
    #[structopt(no_version)]
    Channel(Channel),
    #[structopt(no_version)]
    Job(Job),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["c", "ch", "cha", "chan", "chann", "channe"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat Builder channels
pub enum Channel {
    #[structopt(no_version, aliases = &["c", "cr", "cre", "crea", "creat"])]
    Create(ChannelCreate),
    Demote(ChannelDemote),
    #[structopt(no_version, aliases = &["d", "de", "des", "dest", "destr", "destro"])]
    Destroy(ChannelDestroy),
    #[structopt(no_version, aliases = &["l", "li", "lis"])]
    List(ChannelList),
    Promote(ChannelPromote),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["j", "jo"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat Builder jobs
pub enum Job {
    #[structopt(no_version, aliases = &["c", "ca", "can", "cance", "cancel"])]
    Cancel(JobCancel),
    #[structopt(no_version, aliases = &["d", "de", "dem", "demo", "demot"])]
    Demote(JobDemote),
    #[structopt(no_version, aliases = &["p", "pr", "pro", "prom", "promo", "promot"])]
    Promote(JobPromote),
    #[structopt(no_version, aliases = &["s", "st", "sta", "star"])]
    Start(JobStart),
    #[structopt(no_version, aliases = &["stat", "statu"])]
    Status(JobStatus),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["cl"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat runtime config
pub enum Cli {
    #[structopt(no_version, aliases = &["s", "se", "set", "setu"])]
    /// Sets up the CLI with reasonable defaults
    Setup(CliSetup),
    #[structopt(no_version, aliases = &["c", "co", "com", "comp"])]
    /// Creates command-line completers for your shell
    Completers(CliCompleters),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["co", "con", "conf", "confi"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to a Service's runtime config
pub enum ServiceConfig {
    #[structopt(no_version, aliases = &["ap", "app", "appl"])]
    Apply(ServiceConfigApply),
    #[structopt(no_version, aliases = &["sh", "sho"])]
    Show(ServiceConfigShow),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["f", "fi", "fil"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat files
pub enum File {
    #[structopt(no_version, aliases = &["u", "up", "upl", "uplo", "uploa"])]
    /// Uploads a file to be shared between members of a Service Group
    Upload(FileUpload),
}

/// Commands relating to Habitat users
#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "user", no_version, aliases = &["u", "us", "use"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
pub enum User {
    Key(UserKey),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "key", no_version, aliases = &["k", "ke"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat user keys
pub enum UserKey {
    #[structopt(no_version, aliases = &["g", "ge", "gen", "gene", "gener", "genera", "generat"])]
    Generate(UserKeyGenerate),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["pl", "pla"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to plans and other app-specific configuration
pub enum Plan {
    #[structopt(no_version, aliases = &["i", "in", "ini"])]
    Init(PlanInit),
    #[structopt(no_version, aliases = &["r", "re", "ren", "rend", "rende"])]
    Render(PlanRender),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(no_version, aliases = &["r", "ri", "rin"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat rings
pub enum Ring {
    Key(RingKey),
}

#[derive(ConfigOpt, StructOpt)]
#[structopt(name = "key", no_version, aliases = &["k", "ke"], settings = &[AppSettings::ArgRequiredElseHelp, AppSettings::SubcommandRequiredElseHelp])]
/// Commands relating to Habitat ring keys
pub enum RingKey {
    #[structopt(no_version, aliases = &["e", "ex", "exp", "expo", "expor"])]
    Export(RingKeyExport),
    #[structopt(no_version, aliases = &["i", "im", "imp", "impo", "impor"])]
    Import(RingKeyImport),
    #[structopt(no_version, aliases = &["g", "ge", "gen", "gene", "gener", "genera", "generat"])]
    Generate(RingKeyGenerate),
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Implements parallel traversals over the DOM and flow trees.
//!
//! This code is highly unsafe. Keep this file small and easy to audit.

#![allow(unsafe_code)]

use context::LayoutContext;
use flow::{self, Flow, MutableFlowUtils, PostorderFlowTraversal, PreorderFlowTraversal};
use flow_ref::FlowRef;
use profile_traits::time::{self, TimerMetadata, profile};
use rayon;
use servo_config::opts;
use smallvec::SmallVec;
use std::mem;
use std::sync::atomic::{AtomicIsize, Ordering};
use style::dom::UnsafeNode;
use traversal::{AssignISizes, BubbleISizes};
use traversal::AssignBSizes;

pub use style::parallel::traverse_dom;

/// Traversal chunk size.
const CHUNK_SIZE: usize = 16;

pub type FlowList = SmallVec<[UnsafeNode; CHUNK_SIZE]>;

#[allow(dead_code)]
fn static_assertion(node: UnsafeNode) {
    unsafe {
        let _: UnsafeFlow = ::std::intrinsics::transmute(node);
    }
}

/// Vtable + pointer representation of a Flow trait object.
pub type UnsafeFlow = (usize, usize);

fn null_unsafe_flow() -> UnsafeFlow {
    (0, 0)
}

pub fn mut_owned_flow_to_unsafe_flow(flow: *mut FlowRef) -> UnsafeFlow {
    unsafe {
        mem::transmute::<&Flow, UnsafeFlow>(&**flow)
    }
}

pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow {
    unsafe {
        mem::transmute::<&Flow, UnsafeFlow>(flow)
    }
}

/// Information that we need stored in each flow.
pub struct FlowParallelInfo {
    /// The number of children that still need work done.
    pub children_count: AtomicIsize,
    /// The address of the parent flow.
    pub parent: UnsafeFlow,
}

impl FlowParallelInfo {
    pub fn new() -> FlowParallelInfo {
        FlowParallelInfo {
            children_count: AtomicIsize::new(0),
            parent: null_unsafe_flow(),
        }
    }
}

/// Process current flow and potentially traverse its ancestors.
///
/// If we are the last child that finished processing, recursively process
/// our parent. Else, stop. Also, stop at the root.
///
/// Thus, if we start with all the leaves of a tree, we end up traversing
/// the whole tree bottom-up because each parent will be processed exactly
/// once (by the last child that finishes processing).
///
/// The only communication between siblings is that they both
/// fetch-and-subtract the parent's children count.
fn buttom_up_flow(mut unsafe_flow: UnsafeFlow,
                  assign_bsize_traversal: &AssignBSizes) {
    loop {
        // Get a real flow.
        let flow: &mut Flow = unsafe {
            mem::transmute(unsafe_flow)
        };

        // Perform the appropriate traversal.
        if assign_bsize_traversal.should_process(flow) {
            assign_bsize_traversal.process(flow);
        }

        let base = flow::mut_base(flow);

        // Reset the count of children for the next layout traversal.
        base.parallel.children_count.store(base.children.len() as isize,
                                           Ordering::Relaxed);

        // Possibly enqueue the parent.
        let unsafe_parent = base.parallel.parent;
        if unsafe_parent == null_unsafe_flow() {
            // We're done!
            break
        }

        // No, we're not at the root yet. Then are we the last child
        // of our parent to finish processing? If so, we can continue
        // on with our parent; otherwise, we've gotta wait.
        let parent: &mut Flow = unsafe {
            mem::transmute(unsafe_parent)
        };
        let parent_base = flow::mut_base(parent);
        if parent_base.parallel.children_count.fetch_sub(1, Ordering::Relaxed) == 1 {
            // We were the last child of our parent. Reflow our parent.
            unsafe_flow = unsafe_parent
        } else {
            // Stop.
            break
        }
    }
}

fn top_down_flow<'scope>(unsafe_flows: &[UnsafeFlow],
                         scope: &rayon::Scope<'scope>,
                         assign_isize_traversal: &'scope AssignISizes,
                         assign_bsize_traversal: &'scope AssignBSizes)
{
    let mut discovered_child_flows = FlowList::new();

    for unsafe_flow in unsafe_flows {
        let mut had_children = false;
        unsafe {
            // Get a real flow.
            let flow: &mut Flow = mem::transmute(*unsafe_flow);

            // FIXME(emilio): With the switch to rayon we can no longer
            // access a thread id from here easily. Either instrument
            // rayon (the unstable feature) to get a worker thread
            // identifier, or remove all the layout tinting mode.
            //
            // flow::mut_base(flow).thread_id = proxy.worker_index();

            if assign_isize_traversal.should_process(flow) {
                // Perform the appropriate traversal.
                assign_isize_traversal.process(flow);
            }

            // Possibly enqueue the children.
            for kid in flow::child_iter_mut(flow) {
                had_children = true;
                discovered_child_flows.push(borrowed_flow_to_unsafe_flow(kid));
            }
        }

        // If there were no more children, start assigning block-sizes.
        if !had_children {
            buttom_up_flow(*unsafe_flow, &assign_bsize_traversal)
        }
    }

    if discovered_child_flows.is_empty() {
        return
    }

    if discovered_child_flows.len() <= CHUNK_SIZE {
        // We can handle all the children in this work unit.
        top_down_flow(&discovered_child_flows,
                      scope,
                      &assign_isize_traversal,
                      &assign_bsize_traversal);
    } else {
        // Spawn a new work unit for each chunk after the first.
        let mut chunks = discovered_child_flows.chunks(CHUNK_SIZE);
        let first_chunk = chunks.next();
        for chunk in chunks {
            let nodes = chunk.iter().cloned().collect::<FlowList>();
            scope.spawn(move |scope| {
                top_down_flow(&nodes, scope, &assign_isize_traversal, &assign_bsize_traversal);
            });
        }
        if let Some(chunk) = first_chunk {
            top_down_flow(chunk, scope, &assign_isize_traversal, &assign_bsize_traversal);
        }
    }
}

pub fn traverse_flow_tree_preorder(
        root: &mut Flow,
        profiler_metadata: Option<TimerMetadata>,
        time_profiler_chan: time::ProfilerChan,
        context: &LayoutContext,
        queue: &rayon::ThreadPool) {
    if opts::get().bubble_inline_sizes_separately {
        let bubble_inline_sizes = BubbleISizes { layout_context: &context };
        root.traverse_postorder(&bubble_inline_sizes);
    }

    let assign_isize_traversal = &AssignISizes { layout_context: &context };
    let assign_bsize_traversal = &AssignBSizes { layout_context: &context };
    let nodes = [borrowed_flow_to_unsafe_flow(root)];

    queue.install(move || {
        rayon::scope(move |scope| {
            profile(time::ProfilerCategory::LayoutParallelWarmup,
                    profiler_metadata, time_profiler_chan, move || {
                        top_down_flow(&nodes, scope, assign_isize_traversal, assign_bsize_traversal);
            });
        });
    });
}
<commit_msg>layout: Make -Z show-parallel-layout show something useful again.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Implements parallel traversals over the DOM and flow trees.
//!
//! This code is highly unsafe. Keep this file small and easy to audit.

#![allow(unsafe_code)]

use context::LayoutContext;
use flow::{self, Flow, MutableFlowUtils, PostorderFlowTraversal, PreorderFlowTraversal};
use flow_ref::FlowRef;
use profile_traits::time::{self, TimerMetadata, profile};
use rayon;
use servo_config::opts;
use smallvec::SmallVec;
use std::mem;
use std::sync::atomic::{AtomicIsize, Ordering};
use style::dom::UnsafeNode;
use traversal::{AssignISizes, BubbleISizes};
use traversal::AssignBSizes;

pub use style::parallel::traverse_dom;

/// Traversal chunk size.
const CHUNK_SIZE: usize = 16;

pub type FlowList = SmallVec<[UnsafeNode; CHUNK_SIZE]>;

#[allow(dead_code)]
fn static_assertion(node: UnsafeNode) {
    unsafe {
        let _: UnsafeFlow = ::std::intrinsics::transmute(node);
    }
}

/// Vtable + pointer representation of a Flow trait object.
pub type UnsafeFlow = (usize, usize);

fn null_unsafe_flow() -> UnsafeFlow {
    (0, 0)
}

pub fn mut_owned_flow_to_unsafe_flow(flow: *mut FlowRef) -> UnsafeFlow {
    unsafe {
        mem::transmute::<&Flow, UnsafeFlow>(&**flow)
    }
}

pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow {
    unsafe {
        mem::transmute::<&Flow, UnsafeFlow>(flow)
    }
}

/// Information that we need stored in each flow.
pub struct FlowParallelInfo {
    /// The number of children that still need work done.
    pub children_count: AtomicIsize,
    /// The address of the parent flow.
    pub parent: UnsafeFlow,
}

impl FlowParallelInfo {
    pub fn new() -> FlowParallelInfo {
        FlowParallelInfo {
            children_count: AtomicIsize::new(0),
            parent: null_unsafe_flow(),
        }
    }
}

/// Process current flow and potentially traverse its ancestors.
///
/// If we are the last child that finished processing, recursively process
/// our parent. Else, stop. Also, stop at the root.
///
/// Thus, if we start with all the leaves of a tree, we end up traversing
/// the whole tree bottom-up because each parent will be processed exactly
/// once (by the last child that finishes processing).
///
/// The only communication between siblings is that they both
/// fetch-and-subtract the parent's children count.
fn buttom_up_flow(mut unsafe_flow: UnsafeFlow,
                  assign_bsize_traversal: &AssignBSizes) {
    loop {
        // Get a real flow.
        let flow: &mut Flow = unsafe {
            mem::transmute(unsafe_flow)
        };

        // Perform the appropriate traversal.
        if assign_bsize_traversal.should_process(flow) {
            assign_bsize_traversal.process(flow);
        }

        let base = flow::mut_base(flow);

        // Reset the count of children for the next layout traversal.
        base.parallel.children_count.store(base.children.len() as isize,
                                           Ordering::Relaxed);

        // Possibly enqueue the parent.
        let unsafe_parent = base.parallel.parent;
        if unsafe_parent == null_unsafe_flow() {
            // We're done!
            break
        }

        // No, we're not at the root yet. Then are we the last child
        // of our parent to finish processing? If so, we can continue
        // on with our parent; otherwise, we've gotta wait.
        let parent: &mut Flow = unsafe {
            mem::transmute(unsafe_parent)
        };
        let parent_base = flow::mut_base(parent);
        if parent_base.parallel.children_count.fetch_sub(1, Ordering::Relaxed) == 1 {
            // We were the last child of our parent. Reflow our parent.
            unsafe_flow = unsafe_parent
        } else {
            // Stop.
            break
        }
    }
}

fn top_down_flow<'scope>(unsafe_flows: &[UnsafeFlow],
                         pool: &'scope rayon::ThreadPool,
                         scope: &rayon::Scope<'scope>,
                         assign_isize_traversal: &'scope AssignISizes,
                         assign_bsize_traversal: &'scope AssignBSizes)
{
    let mut discovered_child_flows = FlowList::new();

    for unsafe_flow in unsafe_flows {
        let mut had_children = false;
        unsafe {
            // Get a real flow.
            let flow: &mut Flow = mem::transmute(*unsafe_flow);
            flow::mut_base(flow).thread_id =
                pool.current_thread_index().unwrap() as u8;

            if assign_isize_traversal.should_process(flow) {
                // Perform the appropriate traversal.
                assign_isize_traversal.process(flow);
            }

            // Possibly enqueue the children.
            for kid in flow::child_iter_mut(flow) {
                had_children = true;
                discovered_child_flows.push(borrowed_flow_to_unsafe_flow(kid));
            }
        }

        // If there were no more children, start assigning block-sizes.
        if !had_children {
            buttom_up_flow(*unsafe_flow, &assign_bsize_traversal)
        }
    }

    if discovered_child_flows.is_empty() {
        return
    }

    if discovered_child_flows.len() <= CHUNK_SIZE {
        // We can handle all the children in this work unit.
        top_down_flow(&discovered_child_flows,
                      pool,
                      scope,
                      &assign_isize_traversal,
                      &assign_bsize_traversal);
    } else {
        // Spawn a new work unit for each chunk after the first.
        let mut chunks = discovered_child_flows.chunks(CHUNK_SIZE);
        let first_chunk = chunks.next();
        for chunk in chunks {
            let nodes = chunk.iter().cloned().collect::<FlowList>();
            scope.spawn(move |scope| {
                top_down_flow(&nodes, pool, scope, &assign_isize_traversal, &assign_bsize_traversal);
            });
        }
        if let Some(chunk) = first_chunk {
            top_down_flow(chunk, pool, scope, &assign_isize_traversal, &assign_bsize_traversal);
        }
    }
}

pub fn traverse_flow_tree_preorder(
        root: &mut Flow,
        profiler_metadata: Option<TimerMetadata>,
        time_profiler_chan: time::ProfilerChan,
        context: &LayoutContext,
        queue: &rayon::ThreadPool) {
    if opts::get().bubble_inline_sizes_separately {
        let bubble_inline_sizes = BubbleISizes { layout_context: &context };
        root.traverse_postorder(&bubble_inline_sizes);
    }

    let assign_isize_traversal = &AssignISizes { layout_context: &context };
    let assign_bsize_traversal = &AssignBSizes { layout_context: &context };
    let nodes = [borrowed_flow_to_unsafe_flow(root)];

    queue.install(move || {
        rayon::scope(move |scope| {
            profile(time::ProfilerCategory::LayoutParallelWarmup,
                    profiler_metadata, time_profiler_chan, move || {
                        top_down_flow(&nodes, queue, scope, assign_isize_traversal, assign_bsize_traversal);
            });
        });
    });
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::sync::{
    Arc,
    Mutex,
};
use std::thread;
use std::time::Duration;

use c_ares;
use mio;
use mio_more;

use error::Error;
use eventloop::EventLoopHandle;

// The EventLoop will set up a mio::Poll and use it to wait for the following:
//
// -  messages telling it which file descriptors it should be interested in.
//    These file descriptors are then registered (or deregistered) with the
//    mio::Poll as required.
//
// -  events telling it that something has happened on one of these file
//    descriptors.  When this happens, it tells the c_ares::Channel about it.
//
// -  a message telling it to shut down.
pub struct EventLoop {
    poll: mio::Poll,
    tx_msg_channel: mio_more::channel::Sender<Message>,
    rx_msg_channel: mio_more::channel::Receiver<Message>,
    tracked_fds: HashSet<c_ares::Socket>,
    pub ares_channel: Arc<Mutex<c_ares::Channel>>,
    quit: bool,
}

// Messages for the event loop.
#[derive(Debug)]
pub enum Message {
    // 'Notify me when this file descriptor becomes readable, or writable'.
    // The first bool is for 'readable' and the second is for 'writable'.  It's
    // allowed to set both of these - or neither, meaning 'I am no longer
    // interested in this file descriptor'.
    RegisterInterest(c_ares::Socket, bool, bool),

    // 'Shut down'.
    ShutDown,
}

// A token identifying that the message channel has become available for
// reading.
//
// We use Token(fd) for file descriptors, so this relies on zero not being a
// valid file descriptor for c-ares to use.  Zero is stdin, so that's true.
const CHANNEL: mio::Token = mio::Token(0);

impl EventLoop {
    // Create a new event loop.
    pub fn new(mut options: c_ares::Options) -> Result<EventLoop, Error> {
        // Create a mio::Poll on which to wait for events, and register a
        // channel with it.
        let poll = mio::Poll::new()?;
        let (tx, rx) = mio_more::channel::channel();
        poll.register(
            &rx,
            CHANNEL,
            mio::Ready::readable(),
            mio::PollOpt::edge()
        )?;

        // Whenever c-ares tells us what to do with a file descriptor, we'll
        // send that request along, through the channel we just created.
        let tx_clone = tx.clone();
        let sock_callback =
            move |fd: c_ares::Socket, readable: bool, writable: bool| {
                let _ = tx_clone.send(
                    Message::RegisterInterest(fd, readable, writable));
            };
        options.set_socket_state_callback(sock_callback);

        // Create the c-ares channel.
        let ares_channel = c_ares::Channel::with_options(options)?;
        let locked_channel = Arc::new(Mutex::new(ares_channel));

        // Create and return the event loop.
        let event_loop = EventLoop {
            poll: poll,
            tx_msg_channel: tx,
            rx_msg_channel: rx,
            tracked_fds: HashSet::<c_ares::Socket>::new(),
            ares_channel: locked_channel,
            quit: false,
        };
        Ok(event_loop)
    }

    // Run the event loop.
    pub fn run(self) -> EventLoopHandle {
        let tx_clone = self.tx_msg_channel.clone();
        let join_handle = thread::spawn(|| self.event_loop_thread());
        EventLoopHandle::new(join_handle, tx_clone)
    }

    // Event loop thread - waits for events, and handles them.
    fn event_loop_thread(mut self) {
        let mut events = mio::Events::with_capacity(16);
        loop {
            // Wait for something to happen.
            let timeout = Duration::from_millis(100);
            let results = self.poll
                .poll(&mut events, Some(timeout))
                .expect("poll failed");

            // Process whatever happened.
            match results {
                0 => {
                    // No events - must be a timeout.  Tell c-ares about it.
                    self.ares_channel.lock().unwrap().process_fd(
                        c_ares::SOCKET_BAD,
                        c_ares::SOCKET_BAD
                    );
                },
                _ => {
                    // Process events.  One of them might have asked us to
                    // quit.
                    for event in &events {
                        self.handle_event(&event);
                        if self.quit { return }
                    }
                }
            }
        }
    }

    // Handle a single event.
    fn handle_event(&mut self, event: &mio::Event) {
        match event.token() {
            CHANNEL => {
                // The channel is readable.
                self.handle_messages()
            },

            mio::Token(fd) => {
                // Sockets became readable or writable - tell c-ares.
                let ready = mio::unix::UnixReady::from(event.readiness());
                let error = ready.is_error() || ready.is_hup();
                let rfd = if error || ready.is_readable() {
                    fd as c_ares::Socket
                } else {
                    c_ares::SOCKET_BAD
                };
                let wfd = if error || ready.is_writable() {
                    fd as c_ares::Socket
                } else {
                    c_ares::SOCKET_BAD
                };
                self.ares_channel.lock().unwrap().process_fd(rfd, wfd);
            }
        }
    }

    // Process messages incoming on the channel.
    fn handle_messages(&mut self) {
        loop {
            match self.rx_msg_channel.try_recv() {
                // Instruction to do something with a file descriptor.
                Ok(Message::RegisterInterest(fd, readable, writable)) => {
                    let efd = mio::unix::EventedFd(&fd);
                    if !readable && !writable {
                        self.tracked_fds.remove(&fd);
                        let _ = self.poll.deregister(&efd);
                    } else {
                        assert_ne!(fd, 0);
                        let token = mio::Token(fd as usize);
                        let mut interest = mio::Ready::from(
                            mio::unix::UnixReady::error() |
                                mio::unix::UnixReady::hup()
                        );
                        if readable { interest.insert(mio::Ready::readable()) }
                        if writable { interest.insert(mio::Ready::writable()) }
                        let register_result = if !self.tracked_fds.insert(fd) {
                            self.poll.reregister(
                                &efd,
                                token,
                                interest,
                                mio::PollOpt::level()
                            )
                        } else {
                            self.poll.register(
                                &efd,
                                token,
                                interest,
                                mio::PollOpt::level()
                            )
                        };
                        register_result.expect("failed to register interest");
                    }
                },

                // Instruction to shut down.
                Ok(Message::ShutDown) => {
                    self.quit = true;
                    break
                },

                // No more instructions.
                Err(_) => break,
            }
        }
    }
}
<commit_msg>more meaningful (and futureproof) assertion<commit_after>use std::collections::HashSet;
use std::sync::{
    Arc,
    Mutex,
};
use std::thread;
use std::time::Duration;

use c_ares;
use mio;
use mio_more;

use error::Error;
use eventloop::EventLoopHandle;

// The EventLoop will set up a mio::Poll and use it to wait for the following:
//
// -  messages telling it which file descriptors it should be interested in.
//    These file descriptors are then registered (or deregistered) with the
//    mio::Poll as required.
//
// -  events telling it that something has happened on one of these file
//    descriptors.  When this happens, it tells the c_ares::Channel about it.
//
// -  a message telling it to shut down.
pub struct EventLoop {
    poll: mio::Poll,
    tx_msg_channel: mio_more::channel::Sender<Message>,
    rx_msg_channel: mio_more::channel::Receiver<Message>,
    tracked_fds: HashSet<c_ares::Socket>,
    pub ares_channel: Arc<Mutex<c_ares::Channel>>,
    quit: bool,
}

// Messages for the event loop.
#[derive(Debug)]
pub enum Message {
    // 'Notify me when this file descriptor becomes readable, or writable'.
    // The first bool is for 'readable' and the second is for 'writable'.  It's
    // allowed to set both of these - or neither, meaning 'I am no longer
    // interested in this file descriptor'.
    RegisterInterest(c_ares::Socket, bool, bool),

    // 'Shut down'.
    ShutDown,
}

// A token identifying that the message channel has become available for
// reading.
//
// We use Token(fd) for file descriptors, so this relies on zero not being a
// valid file descriptor for c-ares to use.  Zero is stdin, so that's true.
const CHANNEL: mio::Token = mio::Token(0);

impl EventLoop {
    // Create a new event loop.
    pub fn new(mut options: c_ares::Options) -> Result<EventLoop, Error> {
        // Create a mio::Poll on which to wait for events, and register a
        // channel with it.
        let poll = mio::Poll::new()?;
        let (tx, rx) = mio_more::channel::channel();
        poll.register(
            &rx,
            CHANNEL,
            mio::Ready::readable(),
            mio::PollOpt::edge()
        )?;

        // Whenever c-ares tells us what to do with a file descriptor, we'll
        // send that request along, through the channel we just created.
        let tx_clone = tx.clone();
        let sock_callback =
            move |fd: c_ares::Socket, readable: bool, writable: bool| {
                let _ = tx_clone.send(
                    Message::RegisterInterest(fd, readable, writable));
            };
        options.set_socket_state_callback(sock_callback);

        // Create the c-ares channel.
        let ares_channel = c_ares::Channel::with_options(options)?;
        let locked_channel = Arc::new(Mutex::new(ares_channel));

        // Create and return the event loop.
        let event_loop = EventLoop {
            poll: poll,
            tx_msg_channel: tx,
            rx_msg_channel: rx,
            tracked_fds: HashSet::<c_ares::Socket>::new(),
            ares_channel: locked_channel,
            quit: false,
        };
        Ok(event_loop)
    }

    // Run the event loop.
    pub fn run(self) -> EventLoopHandle {
        let tx_clone = self.tx_msg_channel.clone();
        let join_handle = thread::spawn(|| self.event_loop_thread());
        EventLoopHandle::new(join_handle, tx_clone)
    }

    // Event loop thread - waits for events, and handles them.
    fn event_loop_thread(mut self) {
        let mut events = mio::Events::with_capacity(16);
        loop {
            // Wait for something to happen.
            let timeout = Duration::from_millis(100);
            let results = self.poll
                .poll(&mut events, Some(timeout))
                .expect("poll failed");

            // Process whatever happened.
            match results {
                0 => {
                    // No events - must be a timeout.  Tell c-ares about it.
                    self.ares_channel.lock().unwrap().process_fd(
                        c_ares::SOCKET_BAD,
                        c_ares::SOCKET_BAD
                    );
                },
                _ => {
                    // Process events.  One of them might have asked us to
                    // quit.
                    for event in &events {
                        self.handle_event(&event);
                        if self.quit { return }
                    }
                }
            }
        }
    }

    // Handle a single event.
    fn handle_event(&mut self, event: &mio::Event) {
        match event.token() {
            CHANNEL => {
                // The channel is readable.
                self.handle_messages()
            },

            mio::Token(fd) => {
                // Sockets became readable or writable - tell c-ares.
                let ready = mio::unix::UnixReady::from(event.readiness());
                let error = ready.is_error() || ready.is_hup();
                let rfd = if error || ready.is_readable() {
                    fd as c_ares::Socket
                } else {
                    c_ares::SOCKET_BAD
                };
                let wfd = if error || ready.is_writable() {
                    fd as c_ares::Socket
                } else {
                    c_ares::SOCKET_BAD
                };
                self.ares_channel.lock().unwrap().process_fd(rfd, wfd);
            }
        }
    }

    // Process messages incoming on the channel.
    fn handle_messages(&mut self) {
        loop {
            match self.rx_msg_channel.try_recv() {
                // Instruction to do something with a file descriptor.
                Ok(Message::RegisterInterest(fd, readable, writable)) => {
                    let efd = mio::unix::EventedFd(&fd);
                    if !readable && !writable {
                        self.tracked_fds.remove(&fd);
                        let _ = self.poll.deregister(&efd);
                    } else {
                        let token = mio::Token(fd as usize);
                        assert_ne!(token, CHANNEL);
                        let mut interest = mio::Ready::from(
                            mio::unix::UnixReady::error() |
                                mio::unix::UnixReady::hup()
                        );
                        if readable { interest.insert(mio::Ready::readable()) }
                        if writable { interest.insert(mio::Ready::writable()) }
                        let register_result = if !self.tracked_fds.insert(fd) {
                            self.poll.reregister(
                                &efd,
                                token,
                                interest,
                                mio::PollOpt::level()
                            )
                        } else {
                            self.poll.register(
                                &efd,
                                token,
                                interest,
                                mio::PollOpt::level()
                            )
                        };
                        register_result.expect("failed to register interest");
                    }
                },

                // Instruction to shut down.
                Ok(Message::ShutDown) => {
                    self.quit = true;
                    break
                },

                // No more instructions.
                Err(_) => break,
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Utility wrappers to simplify writing OpenGL code.
//!
//! This crate aspires to provide an abstraction over OpenGL's raw API in order to simplify the
//! task of writing higher-level rendering code for OpenGL. `gl-util` is much in the vein of
//! [glutin](https://github.com/tomaka/glium) and [gfx-rs](https://github.com/gfx-rs/gfx) before
//! it, the main difference being that it is much more poorly constructed and is being developed by
//! someone much less experienced with OpenGL.

extern crate bootstrap_gl as gl;

use gl::{BufferName, BufferTarget, BufferUsage, ClearBufferMask, GlType, VertexArrayName};
use std::mem;

pub use gl::{AttributeLocation, DrawMode};
pub use gl::platform::swap_buffers;

/// Initializes global OpenGL state and creates the OpenGL context needed to perform rendering.
pub fn init() {
    gl::create_context();
}

/// TODO: Take clear mask (and values) as parameters.
pub fn clear() {
    unsafe { gl::clear(ClearBufferMask::Color); }
}

/// Represents a buffer of vertex data and the layout of that data.
///
/// Wraps a vertex buffer object and vertex array object into one struct.
#[derive(Debug)]
pub struct VertexBuffer {
    buffer_name: BufferName,
    vertex_array_name: VertexArrayName,
    len: usize,
    element_len: usize,
}

impl VertexBuffer {
    /// Creates a new `VertexBuffer` object.
    pub fn new() -> VertexBuffer {
        let mut buffer_name = BufferName::null();
        let mut vertex_array_name = VertexArrayName::null();
        unsafe {
            gl::gen_buffers(1, &mut buffer_name);
            gl::gen_vertex_arrays(1, &mut vertex_array_name);
        }

        VertexBuffer {
            buffer_name: buffer_name,
            vertex_array_name: vertex_array_name,
            len: 0,
            element_len: 0,
        }
    }

    /// Fills the buffer with the contents of the data slice.
    pub fn set_data_f32(&mut self, data: &[f32]) {
        self.len = data.len();

        let data_ptr = data.as_ptr() as *const ();
        let byte_count = data.len() * mem::size_of::<f32>();

        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::buffer_data(
                BufferTarget::Array,
                byte_count as isize,
                data_ptr,
                BufferUsage::StaticDraw);
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }

    /// Specifies how the data for a particular vertex attribute is laid out in the buffer.
    pub fn set_attrib_f32(
        &mut self,
        attrib: AttributeLocation,
        elements: usize,
        stride: usize,
        offset: usize
    ) {
        // Calculate the number of elements based on the attribute.
        if stride == 0 {
            self.element_len = (self.len - offset) / elements;
        } else {
            unimplemented!();
        }

        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::bind_vertex_array(self.vertex_array_name);

            gl::enable_vertex_attrib_array(AttributeLocation::from_index(0));
            gl::vertex_attrib_pointer(
                attrib,
                elements as i32,
                GlType::Float,
                false,
                stride as i32,
                offset);

            gl::bind_vertex_array(VertexArrayName::null());
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }

    /// Draws the contents of the vertex buffer to the screen.
    pub fn draw(&self, draw_mode: DrawMode) {
        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::bind_vertex_array(self.vertex_array_name);

            gl::draw_arrays(draw_mode, 0, self.element_len as i32);

            gl::bind_vertex_array(VertexArrayName::null());
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }
}

impl Drop for VertexBuffer {
    fn drop(&mut self) {
        unsafe {
            gl::delete_buffers(1, &mut self.buffer_name);
            gl::delete_vertex_arrays(1, &mut self.vertex_array_name);
        }
    }
}
<commit_msg>Add IndexBuffer And Indexed Rendering<commit_after>//! Utility wrappers to simplify writing OpenGL code.
//!
//! This crate aspires to provide an abstraction over OpenGL's raw API in order to simplify the
//! task of writing higher-level rendering code for OpenGL. `gl-util` is much in the vein of
//! [glutin](https://github.com/tomaka/glium) and [gfx-rs](https://github.com/gfx-rs/gfx) before
//! it, the main difference being that it is much more poorly constructed and is being developed by
//! someone much less experienced with OpenGL.

extern crate bootstrap_gl as gl;

use gl::{BufferName, BufferTarget, BufferUsage, ClearBufferMask, GlType, IndexType, VertexArrayName};
use std::mem;

pub use gl::{AttributeLocation, DrawMode};
pub use gl::platform::swap_buffers;

/// Initializes global OpenGL state and creates the OpenGL context needed to perform rendering.
pub fn init() {
    gl::create_context();
}

/// TODO: Take clear mask (and values) as parameters.
pub fn clear() {
    unsafe { gl::clear(ClearBufferMask::Color); }
}

/// Represents a buffer of vertex data and the layout of that data.
///
/// Wraps a vertex buffer object and vertex array object into one struct.
#[derive(Debug)]
pub struct VertexBuffer {
    buffer_name: BufferName,
    vertex_array_name: VertexArrayName,
    len: usize,
    element_len: usize,
}

impl VertexBuffer {
    /// Creates a new `VertexBuffer` object.
    pub fn new() -> VertexBuffer {
        let mut buffer_name = BufferName::null();
        let mut vertex_array_name = VertexArrayName::null();
        unsafe {
            gl::gen_buffers(1, &mut buffer_name);
            gl::gen_vertex_arrays(1, &mut vertex_array_name);
        }

        VertexBuffer {
            buffer_name: buffer_name,
            vertex_array_name: vertex_array_name,
            len: 0,
            element_len: 0,
        }
    }

    /// Fills the buffer with the contents of the data slice.
    pub fn set_data_f32(&mut self, data: &[f32]) {
        self.len = data.len();

        let data_ptr = data.as_ptr() as *const ();
        let byte_count = data.len() * mem::size_of::<f32>();

        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::buffer_data(
                BufferTarget::Array,
                byte_count as isize,
                data_ptr,
                BufferUsage::StaticDraw);
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }

    /// Specifies how the data for a particular vertex attribute is laid out in the buffer.
    pub fn set_attrib_f32(
        &mut self,
        attrib: AttributeLocation,
        elements: usize,
        stride: usize,
        offset: usize
    ) {
        // Calculate the number of elements based on the attribute.
        if stride == 0 {
            self.element_len = (self.len - offset) / elements;
        } else {
            unimplemented!();
        }

        unsafe {
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::bind_vertex_array(self.vertex_array_name);

            gl::enable_vertex_attrib_array(attrib);
            gl::vertex_attrib_pointer(
                attrib,
                elements as i32,
                GlType::Float,
                false,
                stride as i32,
                offset);

            gl::bind_vertex_array(VertexArrayName::null());
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
        }
    }

    /// Draws the contents of the vertex buffer to the screen.
    pub fn draw(&self, draw_mode: DrawMode) {
        unsafe {
            gl::bind_vertex_array(self.vertex_array_name);
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);

            gl::draw_arrays(draw_mode, 0, self.element_len as i32);

            gl::bind_buffer(BufferTarget::Array, BufferName::null());
            gl::bind_vertex_array(VertexArrayName::null());
        }
    }

    pub fn draw_elements(&self, draw_mode: DrawMode, indices: &IndexBuffer) {
        unsafe {
            gl::bind_vertex_array(self.vertex_array_name);
            gl::bind_buffer(BufferTarget::Array, self.buffer_name);
            gl::bind_buffer(BufferTarget::ElementArray, indices.buffer_name);

            gl::draw_elements(draw_mode, indices.len as i32, IndexType::UnsignedInt, 0);

            gl::bind_buffer(BufferTarget::ElementArray, BufferName::null());
            gl::bind_buffer(BufferTarget::Array, BufferName::null());
            gl::bind_vertex_array(VertexArrayName::null());
        }
    }
}

impl Drop for VertexBuffer {
    fn drop(&mut self) {
        unsafe {
            gl::delete_buffers(1, &mut self.buffer_name);
            gl::delete_vertex_arrays(1, &mut self.vertex_array_name);
        }
    }
}

#[derive(Debug)]
pub struct IndexBuffer {
    buffer_name: BufferName,
    len: usize,
}

impl IndexBuffer {
    // Create a new index buffer.
    pub fn new() -> IndexBuffer {
        let mut buffer_name = BufferName::null();
        unsafe {
            gl::gen_buffers(1, &mut buffer_name);
        }

        IndexBuffer {
            buffer_name: buffer_name,
            len: 0,
        }
    }

    pub fn set_data_u32(&mut self, data: &[u32]) {
        self.len = data.len();

        let data_ptr = data.as_ptr() as *const ();
        let byte_count = data.len() * mem::size_of::<u32>();

        unsafe {
            gl::bind_buffer(BufferTarget::ElementArray, self.buffer_name);
            gl::buffer_data(
                BufferTarget::ElementArray,
                byte_count as isize,
                data_ptr,
                BufferUsage::StaticDraw);
            gl::bind_buffer(BufferTarget::ElementArray, BufferName::null());
        }
    }
}

impl Drop for IndexBuffer {
    fn drop(&mut self) {
        unsafe {
            gl::delete_buffers(1, &mut self.buffer_name);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![no_main]

use std::fs::File;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

use cros_fuzz::fuzz_target;
use devices::virtio::{Block, Interrupt, Queue, VirtioDevice};
use sys_util::{EventFd, GuestAddress, GuestMemory, SharedMemory};

const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.

fuzz_target!(|bytes| {
    let size_u64 = size_of::<u64>();
    let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();

    // The fuzz data is interpreted as:
    // starting index 8 bytes
    // command location 8 bytes
    // command 16 bytes
    // descriptors circular buffer 16 bytes * 3
    if bytes.len() < 4 * size_u64 {
        // Need an index to start.
        return;
    }

    let mut data_image = Cursor::new(bytes);

    let first_index = read_u64(&mut data_image);
    if first_index > MEM_SIZE / DESC_SIZE {
        return;
    }
    let first_offset = first_index * DESC_SIZE;
    if first_offset as usize + size_u64 > bytes.len() {
        return;
    }

    let command_addr = read_u64(&mut data_image);
    if command_addr > MEM_SIZE - CMD_SIZE as u64 {
        return;
    }
    if mem
        .write_all_at_addr(
            &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
            GuestAddress(command_addr as u64),
        )
        .is_err()
    {
        return;
    }

    data_image.seek(SeekFrom::Start(first_offset)).unwrap();
    let desc_table = read_u64(&mut data_image);

    if mem
        .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
        .is_err()
    {
        return;
    }

    let mut q = Queue::new(QUEUE_SIZE);
    q.ready = true;
    q.size = QUEUE_SIZE / 2;
    q.max_size = QUEUE_SIZE;

    let queue_evts: Vec<EventFd> = vec![EventFd::new().unwrap()];
    let queue_fd = queue_evts[0].as_raw_fd();
    let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };

    let shm = SharedMemory::anon().unwrap();
    let disk_file: File = shm.into();
    let mut block = Block::new(Box::new(disk_file), false, None).unwrap();

    block.activate(
        mem,
        Interrupt::new(
            Arc::new(AtomicUsize::new(0)),
            EventFd::new().unwrap(),
            EventFd::new().unwrap(),
            None,   // msix_config
            0xFFFF, // VIRTIO_MSI_NO_VECTOR
        ),
        vec![q],
        queue_evts,
    );

    queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});

fn read_u64<T: Read>(readable: &mut T) -> u64 {
    let mut buf = [0u8; size_of::<u64>()];
    readable.read_exact(&mut buf[..]).unwrap();
    u64::from_le_bytes(buf)
}
<commit_msg>fuzz: block_fuzzer: add sparse param to Block::new<commit_after>// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#![no_main]

use std::fs::File;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::mem::size_of;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;

use cros_fuzz::fuzz_target;
use devices::virtio::{Block, Interrupt, Queue, VirtioDevice};
use sys_util::{EventFd, GuestAddress, GuestMemory, SharedMemory};

const MEM_SIZE: u64 = 256 * 1024 * 1024;
const DESC_SIZE: u64 = 16; // Bytes in one virtio descriptor.
const QUEUE_SIZE: u16 = 16; // Max entries in the queue.
const CMD_SIZE: usize = 16; // Bytes in the command.

fuzz_target!(|bytes| {
    let size_u64 = size_of::<u64>();
    let mem = GuestMemory::new(&[(GuestAddress(0), MEM_SIZE)]).unwrap();

    // The fuzz data is interpreted as:
    // starting index 8 bytes
    // command location 8 bytes
    // command 16 bytes
    // descriptors circular buffer 16 bytes * 3
    if bytes.len() < 4 * size_u64 {
        // Need an index to start.
        return;
    }

    let mut data_image = Cursor::new(bytes);

    let first_index = read_u64(&mut data_image);
    if first_index > MEM_SIZE / DESC_SIZE {
        return;
    }
    let first_offset = first_index * DESC_SIZE;
    if first_offset as usize + size_u64 > bytes.len() {
        return;
    }

    let command_addr = read_u64(&mut data_image);
    if command_addr > MEM_SIZE - CMD_SIZE as u64 {
        return;
    }
    if mem
        .write_all_at_addr(
            &bytes[2 * size_u64..(2 * size_u64) + CMD_SIZE],
            GuestAddress(command_addr as u64),
        )
        .is_err()
    {
        return;
    }

    data_image.seek(SeekFrom::Start(first_offset)).unwrap();
    let desc_table = read_u64(&mut data_image);

    if mem
        .write_all_at_addr(&bytes[32..], GuestAddress(desc_table as u64))
        .is_err()
    {
        return;
    }

    let mut q = Queue::new(QUEUE_SIZE);
    q.ready = true;
    q.size = QUEUE_SIZE / 2;
    q.max_size = QUEUE_SIZE;

    let queue_evts: Vec<EventFd> = vec![EventFd::new().unwrap()];
    let queue_fd = queue_evts[0].as_raw_fd();
    let queue_evt = unsafe { EventFd::from_raw_fd(libc::dup(queue_fd)) };

    let shm = SharedMemory::anon().unwrap();
    let disk_file: File = shm.into();
    let mut block = Block::new(Box::new(disk_file), false, true, None).unwrap();

    block.activate(
        mem,
        Interrupt::new(
            Arc::new(AtomicUsize::new(0)),
            EventFd::new().unwrap(),
            EventFd::new().unwrap(),
            None,   // msix_config
            0xFFFF, // VIRTIO_MSI_NO_VECTOR
        ),
        vec![q],
        queue_evts,
    );

    queue_evt.write(77).unwrap(); // Rings the doorbell, any byte will do.
});

fn read_u64<T: Read>(readable: &mut T) -> u64 {
    let mut buf = [0u8; size_of::<u64>()];
    readable.read_exact(&mut buf[..]).unwrap();
    u64::from_le_bytes(buf)
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Perfect_numbers
#![feature(core)]

fn perfect_number(n: usize) -> bool {
  (1..(n / 2)+1).filter(|&i| n % i == 0).sum::<usize>() == n
}

#[cfg(not(test))]
fn main() {
  for n in (2..10_000).filter(|&n| perfect_number(n)) {
    println!("{}", n);
  }
}

#[test]
fn test_first_four() {
  let nums = (2..10_000).filter(|&n| perfect_number(n))
                              .collect::<Vec<usize>>();
  assert_eq!(nums, [6, 28, 496, 8128]);
}

#[test]
fn test_high_number() {
  assert!( perfect_number(33550336) );
}
<commit_msg>de-featured perfect numbers<commit_after>// Implements http://rosettacode.org/wiki/Perfect_numbers
fn perfect_number(n: usize) -> bool {
  (1..(n / 2)+1).filter(|&i| n % i == 0).fold(0,|a, b| a + b) == n
}

#[cfg(not(test))]
fn main() {
  for n in (2..10_000).filter(|&n| perfect_number(n)) {
    println!("{}", n);
  }
}

#[test]
fn test_first_four() {
  let nums = (2..10_000).filter(|&n| perfect_number(n))
                              .collect::<Vec<usize>>();
  assert_eq!(nums, [6, 28, 496, 8128]);
}

#[test]
fn test_high_number() {
  assert!( perfect_number(33550336) );
}
<|endoftext|>


<commit_before>use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::path::Path;

/// Build configuration. See [CFG].
pub struct Cfg<'a> {
    pub include_prefix: &'a str,
    // Not implemented yet. https://github.com/dtolnay/cxx/issues/417
    #[doc(hidden)]
    pub exported_header_dirs: Vec<&'a Path>,
    #[doc(hidden)]
    pub exported_header_prefixes: Vec<&'a str>,
    #[doc(hidden)]
    pub exported_header_links: Vec<&'a str>,
    marker: PhantomData<*const ()>, // !Send + !Sync
}

/// Global configuration of the current build.
///
///

///
/// ## **`CFG.include_prefix`**
///
/// Presently the only exposed configuration is the `include_prefix`, the prefix
/// at which C++ code from your crate as well as directly dependent crates can
/// access the code generated during this build.
///
/// By default, the `include_prefix` is equal to the name of the current crate.
/// That means if our crate is called `demo` and has Rust source files in a
/// *src/* directory and maybe some handwritten C++ header files in an
/// *include/* directory, then the current crate as well as downstream crates
/// might include them as follows:
///
/// ```
/// # const _: &str = stringify! {
///   // include one of the handwritten headers:
/// #include "demo/include/wow.h"
///
///   // include a header generated from Rust cxx::bridge:
/// #include "demo/src/lib.rs.h"
/// # };
/// ```
///
/// By modifying `CFG.include_prefix` we can substitute a prefix that is
/// different from the crate name if desired. Here we'll change it to
/// `"path/to"` which will make import paths take the form
/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.include_prefix = "path/to";
///
///     cxx_build::bridge("src/lib.rs")
///         .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
///         /* ... */
///         .compile("demo");
/// }
/// ```
///
/// Note that cross-crate imports are only made available between **direct
/// dependencies**. Another crate must directly depend on your crate in order to
/// #include its headers; a transitive dependency is not sufficient.
/// Additionally, headers from a direct dependency are only importable if the
/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers
/// will not be importable from outside of the same crate.
#[cfg(doc)]
pub static mut CFG: Cfg = Cfg {
    include_prefix: "",
    exported_header_dirs: Vec::new(),
    exported_header_prefixes: Vec::new(),
    exported_header_links: Vec::new(),
    marker: PhantomData,
};

impl<'a> Debug for Cfg<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter
            .debug_struct("Cfg")
            .field("include_prefix", &self.include_prefix)
            .finish()
    }
}

#[cfg(not(doc))]
pub use self::r#impl::Cfg::CFG;

#[cfg(not(doc))]
mod r#impl {
    use crate::intern::{intern, InternedString};
    use lazy_static::lazy_static;
    use std::cell::RefCell;
    use std::collections::HashMap;
    use std::fmt::{self, Debug};
    use std::marker::PhantomData;
    use std::ops::{Deref, DerefMut};
    use std::path::Path;
    use std::sync::{PoisonError, RwLock};

    lazy_static! {
        static ref INCLUDE_PREFIX: RwLock<InternedString> = RwLock::new({
            crate::env_os("CARGO_PKG_NAME")
                .map(|pkg| intern(&pkg.to_string_lossy()))
                .unwrap_or_default()
        });
        static ref EXPORTED_HEADER_DIRS: RwLock<Vec<InternedString>> = RwLock::new(Vec::new());
        static ref EXPORTED_HEADER_PREFIXES: RwLock<Vec<InternedString>> = RwLock::new(Vec::new());
        static ref EXPORTED_HEADER_LINKS: RwLock<Vec<InternedString>> = RwLock::new(Vec::new());
    }

    thread_local! {
        // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
        // we can delete this thread local side table and instead make each CFG
        // instance directly own the associated super::Cfg.
        //
        //     #[allow(const_item_mutation)]
        //     pub const CFG: Cfg = Cfg {
        //         cfg: AtomicPtr::new(ptr::null_mut()),
        //     };
        //     pub struct Cfg {
        //         cfg: AtomicPtr<super::Cfg>,
        //     }
        //
        static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
    }

    #[derive(Eq, PartialEq, Hash)]
    struct Handle(*const Cfg<'static>);

    impl<'a> Cfg<'a> {
        fn current() -> super::Cfg<'a> {
            let include_prefix = INCLUDE_PREFIX
                .read()
                .unwrap_or_else(PoisonError::into_inner)
                .str();
            let exported_header_dirs = EXPORTED_HEADER_DIRS
                .read()
                .unwrap_or_else(PoisonError::into_inner)
                .iter()
                .copied()
                .map(InternedString::str)
                .map(Path::new)
                .collect();
            let exported_header_prefixes = EXPORTED_HEADER_PREFIXES
                .read()
                .unwrap_or_else(PoisonError::into_inner)
                .iter()
                .copied()
                .map(InternedString::str)
                .collect();
            let exported_header_links = EXPORTED_HEADER_LINKS
                .read()
                .unwrap_or_else(PoisonError::into_inner)
                .iter()
                .copied()
                .map(InternedString::str)
                .collect();
            super::Cfg {
                include_prefix,
                exported_header_dirs,
                exported_header_prefixes,
                exported_header_links,
                marker: PhantomData,
            }
        }

        const fn handle(self: &Cfg<'a>) -> Handle {
            Handle(<*const Cfg>::cast(self))
        }
    }

    // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
    // drop on the same thread where they were created.
    pub enum Cfg<'a> {
        Mut(super::Cfg<'a>),
        CFG,
    }

    impl<'a> Debug for Cfg<'a> {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            if let Cfg::Mut(cfg) = self {
                Debug::fmt(cfg, formatter)
            } else {
                Debug::fmt(&Cfg::current(), formatter)
            }
        }
    }

    impl<'a> Deref for Cfg<'a> {
        type Target = super::Cfg<'a>;

        fn deref(&self) -> &Self::Target {
            if let Cfg::Mut(cfg) = self {
                cfg
            } else {
                let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
                    &mut **derefs
                        .borrow_mut()
                        .entry(self.handle())
                        .or_insert_with(|| Box::new(Cfg::current()))
                });
                unsafe { &mut *cfg }
            }
        }
    }

    impl<'a> DerefMut for Cfg<'a> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            if let Cfg::CFG = self {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
                *self = Cfg::Mut(Cfg::current());
            }
            match self {
                Cfg::Mut(cfg) => cfg,
                Cfg::CFG => unreachable!(),
            }
        }
    }

    impl<'a> Drop for Cfg<'a> {
        fn drop(&mut self) {
            if let Cfg::Mut(cfg) = self {
                *INCLUDE_PREFIX
                    .write()
                    .unwrap_or_else(PoisonError::into_inner) = intern(cfg.include_prefix);
                *EXPORTED_HEADER_DIRS
                    .write()
                    .unwrap_or_else(PoisonError::into_inner) = cfg
                    .exported_header_dirs
                    .iter()
                    .copied()
                    .map(|path| intern(&path.to_string_lossy()))
                    .collect();
                *EXPORTED_HEADER_PREFIXES
                    .write()
                    .unwrap_or_else(PoisonError::into_inner) = cfg
                    .exported_header_prefixes
                    .iter()
                    .copied()
                    .map(intern)
                    .collect();
                *EXPORTED_HEADER_LINKS
                    .write()
                    .unwrap_or_else(PoisonError::into_inner) = cfg
                    .exported_header_links
                    .iter()
                    .copied()
                    .map(intern)
                    .collect();
            } else {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
            }
        }
    }
}
<commit_msg>Combine current cfg into one rwlock<commit_after>use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::path::Path;

/// Build configuration. See [CFG].
pub struct Cfg<'a> {
    pub include_prefix: &'a str,
    // Not implemented yet. https://github.com/dtolnay/cxx/issues/417
    #[doc(hidden)]
    pub exported_header_dirs: Vec<&'a Path>,
    #[doc(hidden)]
    pub exported_header_prefixes: Vec<&'a str>,
    #[doc(hidden)]
    pub exported_header_links: Vec<&'a str>,
    marker: PhantomData<*const ()>, // !Send + !Sync
}

/// Global configuration of the current build.
///
///

///
/// ## **`CFG.include_prefix`**
///
/// Presently the only exposed configuration is the `include_prefix`, the prefix
/// at which C++ code from your crate as well as directly dependent crates can
/// access the code generated during this build.
///
/// By default, the `include_prefix` is equal to the name of the current crate.
/// That means if our crate is called `demo` and has Rust source files in a
/// *src/* directory and maybe some handwritten C++ header files in an
/// *include/* directory, then the current crate as well as downstream crates
/// might include them as follows:
///
/// ```
/// # const _: &str = stringify! {
///   // include one of the handwritten headers:
/// #include "demo/include/wow.h"
///
///   // include a header generated from Rust cxx::bridge:
/// #include "demo/src/lib.rs.h"
/// # };
/// ```
///
/// By modifying `CFG.include_prefix` we can substitute a prefix that is
/// different from the crate name if desired. Here we'll change it to
/// `"path/to"` which will make import paths take the form
/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
///
/// ```no_run
/// // build.rs
///
/// use cxx_build::CFG;
///
/// fn main() {
///     CFG.include_prefix = "path/to";
///
///     cxx_build::bridge("src/lib.rs")
///         .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
///         /* ... */
///         .compile("demo");
/// }
/// ```
///
/// Note that cross-crate imports are only made available between **direct
/// dependencies**. Another crate must directly depend on your crate in order to
/// #include its headers; a transitive dependency is not sufficient.
/// Additionally, headers from a direct dependency are only importable if the
/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers
/// will not be importable from outside of the same crate.
#[cfg(doc)]
pub static mut CFG: Cfg = Cfg {
    include_prefix: "",
    exported_header_dirs: Vec::new(),
    exported_header_prefixes: Vec::new(),
    exported_header_links: Vec::new(),
    marker: PhantomData,
};

impl<'a> Debug for Cfg<'a> {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter
            .debug_struct("Cfg")
            .field("include_prefix", &self.include_prefix)
            .finish()
    }
}

#[cfg(not(doc))]
pub use self::r#impl::Cfg::CFG;

#[cfg(not(doc))]
mod r#impl {
    use crate::intern::{intern, InternedString};
    use lazy_static::lazy_static;
    use std::cell::RefCell;
    use std::collections::HashMap;
    use std::fmt::{self, Debug};
    use std::marker::PhantomData;
    use std::ops::{Deref, DerefMut};
    use std::path::Path;
    use std::sync::{PoisonError, RwLock};

    struct CurrentCfg {
        include_prefix: InternedString,
        exported_header_dirs: Vec<InternedString>,
        exported_header_prefixes: Vec<InternedString>,
        exported_header_links: Vec<InternedString>,
    }

    impl CurrentCfg {
        fn default() -> Self {
            let include_prefix = crate::env_os("CARGO_PKG_NAME")
                .map(|pkg| intern(&pkg.to_string_lossy()))
                .unwrap_or_default();
            let exported_header_dirs = Vec::new();
            let exported_header_prefixes = Vec::new();
            let exported_header_links = Vec::new();
            CurrentCfg {
                include_prefix,
                exported_header_dirs,
                exported_header_prefixes,
                exported_header_links,
            }
        }
    }

    lazy_static! {
        static ref CURRENT: RwLock<CurrentCfg> = RwLock::new(CurrentCfg::default());
    }

    thread_local! {
        // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
        // we can delete this thread local side table and instead make each CFG
        // instance directly own the associated super::Cfg.
        //
        //     #[allow(const_item_mutation)]
        //     pub const CFG: Cfg = Cfg {
        //         cfg: AtomicPtr::new(ptr::null_mut()),
        //     };
        //     pub struct Cfg {
        //         cfg: AtomicPtr<super::Cfg>,
        //     }
        //
        static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
    }

    #[derive(Eq, PartialEq, Hash)]
    struct Handle(*const Cfg<'static>);

    impl<'a> Cfg<'a> {
        fn current() -> super::Cfg<'a> {
            let current = CURRENT.read().unwrap_or_else(PoisonError::into_inner);
            let include_prefix = current.include_prefix.str();
            let exported_header_dirs = current
                .exported_header_dirs
                .iter()
                .copied()
                .map(InternedString::str)
                .map(Path::new)
                .collect();
            let exported_header_prefixes = current
                .exported_header_prefixes
                .iter()
                .copied()
                .map(InternedString::str)
                .collect();
            let exported_header_links = current
                .exported_header_links
                .iter()
                .copied()
                .map(InternedString::str)
                .collect();
            super::Cfg {
                include_prefix,
                exported_header_dirs,
                exported_header_prefixes,
                exported_header_links,
                marker: PhantomData,
            }
        }

        const fn handle(self: &Cfg<'a>) -> Handle {
            Handle(<*const Cfg>::cast(self))
        }
    }

    // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
    // drop on the same thread where they were created.
    pub enum Cfg<'a> {
        Mut(super::Cfg<'a>),
        CFG,
    }

    impl<'a> Debug for Cfg<'a> {
        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            if let Cfg::Mut(cfg) = self {
                Debug::fmt(cfg, formatter)
            } else {
                Debug::fmt(&Cfg::current(), formatter)
            }
        }
    }

    impl<'a> Deref for Cfg<'a> {
        type Target = super::Cfg<'a>;

        fn deref(&self) -> &Self::Target {
            if let Cfg::Mut(cfg) = self {
                cfg
            } else {
                let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
                    &mut **derefs
                        .borrow_mut()
                        .entry(self.handle())
                        .or_insert_with(|| Box::new(Cfg::current()))
                });
                unsafe { &mut *cfg }
            }
        }
    }

    impl<'a> DerefMut for Cfg<'a> {
        fn deref_mut(&mut self) -> &mut Self::Target {
            if let Cfg::CFG = self {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
                *self = Cfg::Mut(Cfg::current());
            }
            match self {
                Cfg::Mut(cfg) => cfg,
                Cfg::CFG => unreachable!(),
            }
        }
    }

    impl<'a> Drop for Cfg<'a> {
        fn drop(&mut self) {
            if let Cfg::Mut(cfg) = self {
                let mut current = CURRENT.write().unwrap_or_else(PoisonError::into_inner);
                current.include_prefix = intern(cfg.include_prefix);
                current.exported_header_dirs = cfg
                    .exported_header_dirs
                    .iter()
                    .copied()
                    .map(|path| intern(&path.to_string_lossy()))
                    .collect();
                current.exported_header_prefixes = cfg
                    .exported_header_prefixes
                    .iter()
                    .copied()
                    .map(intern)
                    .collect();
                current.exported_header_links = cfg
                    .exported_header_links
                    .iter()
                    .copied()
                    .map(intern)
                    .collect();
            } else {
                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
            }
        }
    }
}
<|endoftext|>


<commit_before>
use std::io::{self, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::OsString;
use std::error::Error;
use std::collections::HashSet;

use walkdir::WalkDir;
use regex::Regex;
use itertools::Itertools;

use file_node::FileNode;
use hash_graph::HashGraph;
use super::*;

// ----------------------------------------------------------------------------

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &FileNode,
                              parent_file: &Path,
                              system_search_paths: &[PathBuf])
                              -> FileNode {

    let local_dir = parent_file.parent().unwrap(); // strip the file name

    let normalized_path = path_utils::normalize_path_separators(&include.path);

    match path_utils::convert_to_absolute_path(&normalized_path, local_dir, system_search_paths) {
        None => {

            println!("Unable to locate {:?}", &include.path);
            println!("Included from file {:?}\n", parent_file);
            include.clone()
        }
        Some(path_buf) => FileNode::from_path(&path_buf, include.is_system),
    }
}

// -----------------------------------------------------------------------------

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<FileNode>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The second (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        // Notes:
        // (?m:^[[:blank:]]*) => empty space at line start, multi-line mode, non-capturing group.
        static ref RE: Regex =
        Regex::new(r##"(?m:^[[:blank:]]*)#[[:blank:]]*include[[:blank:]]*([<"])(.*)[>"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    if let Some(cap) = RE.captures(&text) {
        let is_system_include = cap.get(1).map_or(false, |sym| sym.as_str() == "<");

        if let Some(include_name) = cap.get(2) {
            includes.push(FileNode::new(include_name.as_str(), is_system_include));
        }
    }

    // println!("Found {} includes in {}", includes.len(), &file.display());

    Ok(includes)
}

// -----------------------------------------------------------------------------

// Core include searching loop
pub fn find_includes_in_tree(root_dir: &Path,
                             search_paths: &Vec<PathBuf>,
                             extensions: &HashSet<OsString>,
                             parse_user_includes: bool,
                             parse_system_includes: bool,
                             exclude_regex: &Option<Regex>) -> HashGraph<FileNode> {
    // Collect all the files to scan in a HashSet
    // Note: is_hidden() is currently hiding paths that start with './', so don't use it yet.
    let input_queue = WalkDir::new(root_dir).into_iter()
        //.filter_entry(|e| !path_utils::is_hidden(e))
        .filter_map(|entry| match entry {
            Err(what) => {println!("Error reading directory: {}", what.description()); None},
            Ok(val) => Some(val),
        })
        .map(|entry| PathBuf::from(entry.path()))
        .filter(|path| path.extension().map_or(false, |ext| extensions.contains(ext)))
        .filter(|path| !path_utils::filename_matches_regex(&exclude_regex, path))
        .collect::<HashSet<_>>();

    // Graph of all the tracked files
    let mut hash_graph = HashGraph::<FileNode>::new();

    for path_buf in input_queue {
        let parent_file = path_buf.as_path();
        let includes_result = scan_file_for_includes(parent_file);
        match includes_result {
            Ok(includes) => {

                // Convert relative includes to absolute includes
                includes.iter()
                    .filter(|inc| (!inc.is_system && parse_user_includes)
                        || (inc.is_system && parse_system_includes))
                    .filter(|inc| !path_utils::name_matches_regex(&exclude_regex, &inc.path))
                    .map(|inc| find_absolute_include_path(inc, parent_file, &search_paths))
                    .foreach(|inc| {
                        // Add an edge to the graph
                        let src_node = FileNode::from_path(parent_file, false);
                        let dst_node = FileNode::from_path(&inc.path,inc.is_system);
                        hash_graph.add_edge(src_node, dst_node);
                    });
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", parent_file, err);
            }
        }
    }

    hash_graph
}<commit_msg>Added failing unit test<commit_after>
use std::io::{self, Read};
use std::fs::File;
use std::path::{Path, PathBuf};
use std::ffi::OsString;
use std::error::Error;
use std::collections::HashSet;

use walkdir::WalkDir;
use regex::Regex;
use itertools::Itertools;

use file_node::FileNode;
use hash_graph::HashGraph;
use super::*;

// ----------------------------------------------------------------------------

// Convert a relative include path (e.g. <Windows.h>) into an absolute path.
fn find_absolute_include_path(include: &FileNode,
                              parent_file: &Path,
                              system_search_paths: &[PathBuf])
                              -> FileNode {

    let local_dir = parent_file.parent().unwrap(); // strip the file name

    let normalized_path = path_utils::normalize_path_separators(&include.path);

    match path_utils::convert_to_absolute_path(&normalized_path, local_dir, system_search_paths) {
        None => {

            println!("Unable to locate {:?}", &include.path);
            println!("Included from file {:?}\n", parent_file);
            include.clone()
        }
        Some(path_buf) => FileNode::from_path(&path_buf, include.is_system),
    }
}

// -----------------------------------------------------------------------------

// Return a list of #include statements found in the file
fn scan_file_for_includes(file: &Path) -> Result<Vec<FileNode>, io::Error> {
    let mut f = File::open(file)?;
    let mut text = String::new();
    f.read_to_string(&mut text)?;

    let mut includes = Vec::new();

    // Use a regex to search for '#include ...' lines.
    // The second (...) capture group isolates just the text, not the "" or <> symbols.
    lazy_static! {
        // Notes:
        // (?m:^[[:blank:]]*) => empty space at line start, multi-line mode, non-capturing group.
        static ref RE: Regex =
        Regex::new(r##"(?m:^[[:blank:]]*)#[[:blank:]]*include[[:blank:]]*([<"])(.*)[>"]"##).unwrap();
    }

    // cap.at(1) is an angle brace or double quote, to determine user or system include.
    // cap.at(2) is the include file name.
    if let Some(cap) = RE.captures(&text) {
        let is_system_include = cap.get(1).map_or(false, |sym| sym.as_str() == "<");

        if let Some(include_name) = cap.get(2) {
            includes.push(FileNode::new(include_name.as_str(), is_system_include));
        }
    }

    // println!("Found {} includes in {}", includes.len(), &file.display());

    Ok(includes)
}

// -----------------------------------------------------------------------------

// Core include searching loop
pub fn find_includes_in_tree(root_dir: &Path,
                             search_paths: &Vec<PathBuf>,
                             extensions: &HashSet<OsString>,
                             parse_user_includes: bool,
                             parse_system_includes: bool,
                             exclude_regex: &Option<Regex>) -> HashGraph<FileNode> {
    // Collect all the files to scan in a HashSet
    // Note: is_hidden() is currently hiding paths that start with './', so don't use it yet.
    let input_queue = WalkDir::new(root_dir).into_iter()
        //.filter_entry(|e| !path_utils::is_hidden(e))
        .filter_map(|entry| match entry {
            Err(what) => {println!("Error reading directory: {}", what.description()); None},
            Ok(val) => Some(val),
        })
        .map(|entry| PathBuf::from(entry.path()))
        .filter(|path| path.extension().map_or(false, |ext| extensions.contains(ext)))
        .filter(|path| !path_utils::filename_matches_regex(&exclude_regex, path))
        .collect::<HashSet<_>>();

    // Graph of all the tracked files
    let mut hash_graph = HashGraph::<FileNode>::new();

    for path_buf in input_queue {
        let parent_file = path_buf.as_path();
        let includes_result = scan_file_for_includes(parent_file);
        match includes_result {
            Ok(includes) => {

                // Convert relative includes to absolute includes
                includes.iter()
                    .filter(|inc| (!inc.is_system && parse_user_includes)
                        || (inc.is_system && parse_system_includes))
                    .filter(|inc| !path_utils::name_matches_regex(&exclude_regex, &inc.path))
                    .map(|inc| find_absolute_include_path(inc, parent_file, &search_paths))
                    .foreach(|inc| {
                        // Add an edge to the graph
                        let src_node = FileNode::from_path(parent_file, false);
                        let dst_node = FileNode::from_path(&inc.path,inc.is_system);
                        hash_graph.add_edge(src_node, dst_node);
                    });
            }
            Err(err) => {
                println!("Unable to process file {:?}: {}", parent_file, err);
            }
        }
    }

    hash_graph
}

mod test {

    use super::*;

    #[test]
    fn parse_user_includes() {
        let testdata_dir = env::current_dir().unwrap().join("example_tree");

        let mut search_paths = Vec::new();
        search_paths.push(PathBuf::from(&testdata_dir));

        let mut extensions = HashSet::new();
        extensions.insert(OsString::from("h"));
        extensions.insert(OsString::from("cpp"));

        let hash_graph = find_includes_in_tree(&testdata_dir,
                                               &search_paths,
                                               &extensions,
                                               true,
                                               false,
                                               &None);

        assert_eq!(hash_graph.graph.node_count(), 5);
    }

    #[test]
    fn parse_all_includes() {
        let testdata_dir = env::current_dir().unwrap().join("example_tree");

        let mut search_paths = Vec::new();
        search_paths.push(PathBuf::from(&testdata_dir));

        let mut extensions = HashSet::new();
        extensions.insert(OsString::from("h"));
        extensions.insert(OsString::from("cpp"));

        let hash_graph = find_includes_in_tree(&testdata_dir,
                                               &search_paths,
                                               &extensions,
                                               true,
                                               true,
                                               &None);

        assert_eq!(hash_graph.graph.node_count(), 10);
    }

}<|endoftext|>