<commit_before>use serialize::Decodable;
use std::collections::HashMap;
use std::str;
use toml;
use url;

use core::{SourceId, GitKind};
use core::manifest::{LibKind, Lib, Profile};
use core::{Summary, Manifest, Target, Dependency, PackageId};
use core::source::{Location, Local, Remote};
use util::{CargoResult, Require, human};

pub fn to_manifest(contents: &[u8],
                   source_id: &SourceId) -> CargoResult<(Manifest, Vec<Path>)> {
    let contents = try!(str::from_utf8(contents).require(|| {
        human("Cargo.toml is not valid UTF-8")
    }));
    let root = try!(parse(contents, "Cargo.toml"));
    let mut d = toml::Decoder::new(toml::Table(root));
    let toml_manifest: TomlManifest = match Decodable::decode(&mut d) {
        Ok(t) => t,
        Err(e) => return Err(human(format!("Cargo.toml is not a valid \
                                            manifest\n\n{}", e)))
    };

    toml_manifest.to_manifest(source_id).map_err(|err| {
        human(format!("Cargo.toml is not a valid manifest\n\n{}", err))
    })
}

pub fn parse(toml: &str, file: &str) -> CargoResult<toml::Table> {
    let mut parser = toml::Parser::new(toml.as_slice());
    match parser.parse() {
        Some(toml) => Ok(toml),
        None => {
            let mut error_str = format!("could not parse input TOML\n");
            for error in parser.errors.iter() {
                let (loline, locol) = parser.to_linecol(error.lo);
                let (hiline, hicol) = parser.to_linecol(error.hi);
                error_str.push_str(format!("{}:{}:{}{} {}\n",
                                           file,
                                           loline + 1, locol + 1,
                                           if loline != hiline || locol != hicol {
                                               format!("-{}:{}", hiline + 1,
                                                       hicol + 1)
                                           } else {
                                               "".to_string()
                                           },
                                           error.desc).as_slice());
            }
            Err(human(error_str))
        }
    }
}

type TomlLibTarget = TomlTarget;
type TomlBinTarget = TomlTarget;

/*
 * TODO: Make all struct fields private
 */

#[deriving(Encodable,Decodable,PartialEq,Clone,Show)]
pub enum TomlDependency {
    SimpleDep(String),
    DetailedDep(DetailedTomlDependency)
}

#[deriving(Encodable,Decodable,PartialEq,Clone,Show)]
pub struct DetailedTomlDependency {
    version: Option<String>,
    path: Option<String>,
    git: Option<String>,
    branch: Option<String>,
    tag: Option<String>,
    rev: Option<String>
}

#[deriving(Encodable,Decodable,PartialEq,Clone)]
pub struct TomlManifest {
    package: Option<Box<TomlProject>>,
    project: Option<Box<TomlProject>>,
    lib: Option<Vec<TomlLibTarget>>,
    bin: Option<Vec<TomlBinTarget>>,
    dependencies: Option<HashMap<String, TomlDependency>>,
}

#[deriving(Decodable,Encodable,PartialEq,Clone,Show)]
pub struct TomlProject {
    pub name: String,
    // FIXME #54: should be a Version to be able to be Decodable'd directly.
    pub version: String,
    pub authors: Vec<String>,
    build: Option<String>,
}

impl TomlProject {
    pub fn to_package_id(&self, namespace: &Location) -> CargoResult<PackageId> {
        PackageId::new(self.name.as_slice(), self.version.as_slice(), namespace)
    }
}

impl TomlManifest {
    pub fn to_manifest(&self, source_id: &SourceId)
        -> CargoResult<(Manifest, Vec<Path>)>
    {
        let mut sources = vec!();
        let mut nested_paths = vec!();

        // Get targets
        let targets = normalize(self.lib.as_ref().map(|l| l.as_slice()),
                                self.bin.as_ref().map(|b| b.as_slice()));

        if targets.is_empty() {
            debug!("manifest has no build targets; project={}", self.project);
        }

        let mut deps = Vec::new();

        fn to_location(s: &str) -> Location {
            if s.starts_with("file:") {
                Local(Path::new(s.slice_from(5)))
            } else {
                // TODO: Don't unwrap here
                Remote(url::from_str(s).unwrap())
            }
        }

        // Collect the deps
        match self.dependencies {
            Some(ref dependencies) => {
                for (n, v) in dependencies.iter() {
                    let (version, source_id) = match *v {
                        SimpleDep(ref string) => {
                            (Some(string.clone()), SourceId::for_central())
                        },
                        DetailedDep(ref details) => {
                            let reference = details.branch.as_ref().map(|b| b.clone())
                                .or_else(|| details.tag.as_ref().map(|t| t.clone()))
                                .or_else(|| details.rev.as_ref().map(|t| t.clone()))
                                .unwrap_or_else(|| "master".to_str());

                            let new_source_id = details.git.as_ref().map(|git| {
                                let kind = GitKind(reference.clone());
                                let loc = to_location(git.as_slice());
                                let source_id = SourceId::new(kind, loc);
                                // TODO: Don't do this for path
                                sources.push(source_id.clone());
                                source_id
                            }).or_else(|| {
                                details.path.as_ref().map(|path| {
                                    nested_paths.push(Path::new(path.as_slice()));
                                    source_id.clone()
                                })
                            }).unwrap_or(SourceId::for_central());

                            (details.version.clone(), new_source_id)
                        }
                    };

                    deps.push(try!(Dependency::parse(n.as_slice(),
                                                     version.as_ref().map(|v| v.as_slice()),
                                                     &source_id)))
                }
            }
            None => ()
        }

        let project = self.project.as_ref().or_else(|| self.package.as_ref());
        let project = try!(project.require(|| human("No `package` or `project` section found.")));

        Ok((Manifest::new(
                &Summary::new(&try!(project.to_package_id(source_id.get_location())),
                              deps.as_slice()),
                targets.as_slice(),
                &Path::new("target"),
                sources,
                project.build.clone()),
           nested_paths))
    }
}

#[deriving(Decodable,Encodable,PartialEq,Clone,Show)]
struct TomlTarget {
    name: String,
    crate_type: Option<Vec<String>>,
    path: Option<String>,
    test: Option<bool>
}

fn normalize(lib: Option<&[TomlLibTarget]>,
             bin: Option<&[TomlBinTarget]>) -> Vec<Target> {
    log!(4, "normalizing toml targets; lib={}; bin={}", lib, bin);

    fn target_profiles(target: &TomlTarget) -> Vec<Profile> {
        let mut ret = vec!(Profile::default("compile"));

        match target.test {
            Some(true) | None => ret.push(Profile::default("test").test(true)),
            _ => {}
        };

        ret
    }

    fn lib_targets(dst: &mut Vec<Target>, libs: &[TomlLibTarget]) {
        let l = &libs[0];
        let path = l.path.clone().unwrap_or_else(|| format!("src/{}.rs", l.name));
        let crate_types = l.crate_type.clone().and_then(|kinds| {
            LibKind::from_strs(kinds).ok()
        }).unwrap_or_else(|| vec!(Lib));

        for profile in target_profiles(l).iter() {
            dst.push(Target::lib_target(l.name.as_slice(), crate_types.clone(),
                                        &Path::new(path.as_slice()), profile));
        }
    }

    fn bin_targets(dst: &mut Vec<Target>, bins: &[TomlBinTarget],
                   default: |&TomlBinTarget| -> String) {
        for bin in bins.iter() {
            let path = bin.path.clone().unwrap_or_else(|| default(bin));

            for profile in target_profiles(bin).iter() {
                dst.push(Target::bin_target(bin.name.as_slice(), &Path::new(path.as_slice()), profile));
            }
        }
    }

    let mut ret = Vec::new();

    match (lib, bin) {
        (Some(ref libs), Some(ref bins)) => {
            lib_targets(&mut ret, libs.as_slice());
            bin_targets(&mut ret, bins.as_slice(),
                        |bin| format!("src/bin/{}.rs", bin.name));
        },
        (Some(ref libs), None) => {
            lib_targets(&mut ret, libs.as_slice());
        },
        (None, Some(ref bins)) => {
            bin_targets(&mut ret, bins.as_slice(),
                        |bin| format!("src/{}.rs", bin.name));
        },
        (None, None) => ()
    }

    ret
}
<commit_msg>100 chars<commit_after>use serialize::Decodable;
use std::collections::HashMap;
use std::str;
use toml;
use url;

use core::{SourceId, GitKind};
use core::manifest::{LibKind, Lib, Profile};
use core::{Summary, Manifest, Target, Dependency, PackageId};
use core::source::{Location, Local, Remote};
use util::{CargoResult, Require, human};

pub fn to_manifest(contents: &[u8],
                   source_id: &SourceId) -> CargoResult<(Manifest, Vec<Path>)> {
    let contents = try!(str::from_utf8(contents).require(|| {
        human("Cargo.toml is not valid UTF-8")
    }));
    let root = try!(parse(contents, "Cargo.toml"));
    let mut d = toml::Decoder::new(toml::Table(root));
    let toml_manifest: TomlManifest = match Decodable::decode(&mut d) {
        Ok(t) => t,
        Err(e) => return Err(human(format!("Cargo.toml is not a valid \
                                            manifest\n\n{}", e)))
    };

    toml_manifest.to_manifest(source_id).map_err(|err| {
        human(format!("Cargo.toml is not a valid manifest\n\n{}", err))
    })
}

pub fn parse(toml: &str, file: &str) -> CargoResult<toml::Table> {
    let mut parser = toml::Parser::new(toml.as_slice());
    match parser.parse() {
        Some(toml) => Ok(toml),
        None => {
            let mut error_str = format!("could not parse input TOML\n");
            for error in parser.errors.iter() {
                let (loline, locol) = parser.to_linecol(error.lo);
                let (hiline, hicol) = parser.to_linecol(error.hi);
                error_str.push_str(format!("{}:{}:{}{} {}\n",
                                           file,
                                           loline + 1, locol + 1,
                                           if loline != hiline || locol != hicol {
                                               format!("-{}:{}", hiline + 1,
                                                       hicol + 1)
                                           } else {
                                               "".to_string()
                                           },
                                           error.desc).as_slice());
            }
            Err(human(error_str))
        }
    }
}

type TomlLibTarget = TomlTarget;
type TomlBinTarget = TomlTarget;

/*
 * TODO: Make all struct fields private
 */

#[deriving(Encodable,Decodable,PartialEq,Clone,Show)]
pub enum TomlDependency {
    SimpleDep(String),
    DetailedDep(DetailedTomlDependency)
}

#[deriving(Encodable,Decodable,PartialEq,Clone,Show)]
pub struct DetailedTomlDependency {
    version: Option<String>,
    path: Option<String>,
    git: Option<String>,
    branch: Option<String>,
    tag: Option<String>,
    rev: Option<String>
}

#[deriving(Encodable,Decodable,PartialEq,Clone)]
pub struct TomlManifest {
    package: Option<Box<TomlProject>>,
    project: Option<Box<TomlProject>>,
    lib: Option<Vec<TomlLibTarget>>,
    bin: Option<Vec<TomlBinTarget>>,
    dependencies: Option<HashMap<String, TomlDependency>>,
}

#[deriving(Decodable,Encodable,PartialEq,Clone,Show)]
pub struct TomlProject {
    pub name: String,
    // FIXME #54: should be a Version to be able to be Decodable'd directly.
    pub version: String,
    pub authors: Vec<String>,
    build: Option<String>,
}

impl TomlProject {
    pub fn to_package_id(&self, namespace: &Location) -> CargoResult<PackageId> {
        PackageId::new(self.name.as_slice(), self.version.as_slice(), namespace)
    }
}

impl TomlManifest {
    pub fn to_manifest(&self, source_id: &SourceId)
        -> CargoResult<(Manifest, Vec<Path>)>
    {
        let mut sources = vec!();
        let mut nested_paths = vec!();

        // Get targets
        let targets = normalize(self.lib.as_ref().map(|l| l.as_slice()),
                                self.bin.as_ref().map(|b| b.as_slice()));

        if targets.is_empty() {
            debug!("manifest has no build targets; project={}", self.project);
        }

        let mut deps = Vec::new();

        fn to_location(s: &str) -> Location {
            if s.starts_with("file:") {
                Local(Path::new(s.slice_from(5)))
            } else {
                // TODO: Don't unwrap here
                Remote(url::from_str(s).unwrap())
            }
        }

        // Collect the deps
        match self.dependencies {
            Some(ref dependencies) => {
                for (n, v) in dependencies.iter() {
                    let (version, source_id) = match *v {
                        SimpleDep(ref string) => {
                            (Some(string.clone()), SourceId::for_central())
                        },
                        DetailedDep(ref details) => {
                            let reference = details.branch.as_ref().map(|b| b.clone())
                                .or_else(|| details.tag.as_ref().map(|t| t.clone()))
                                .or_else(|| details.rev.as_ref().map(|t| t.clone()))
                                .unwrap_or_else(|| "master".to_str());

                            let new_source_id = details.git.as_ref().map(|git| {
                                let kind = GitKind(reference.clone());
                                let loc = to_location(git.as_slice());
                                let source_id = SourceId::new(kind, loc);
                                // TODO: Don't do this for path
                                sources.push(source_id.clone());
                                source_id
                            }).or_else(|| {
                                details.path.as_ref().map(|path| {
                                    nested_paths.push(Path::new(path.as_slice()));
                                    source_id.clone()
                                })
                            }).unwrap_or(SourceId::for_central());

                            (details.version.clone(), new_source_id)
                        }
                    };

                    deps.push(try!(Dependency::parse(n.as_slice(),
                                                     version.as_ref().map(|v| v.as_slice()),
                                                     &source_id)))
                }
            }
            None => ()
        }

        let project = self.project.as_ref().or_else(|| self.package.as_ref());
        let project = try!(project.require(|| human("No `package` or `project` section found.")));

        Ok((Manifest::new(
                &Summary::new(&try!(project.to_package_id(source_id.get_location())),
                              deps.as_slice()),
                targets.as_slice(),
                &Path::new("target"),
                sources,
                project.build.clone()),
           nested_paths))
    }
}

#[deriving(Decodable,Encodable,PartialEq,Clone,Show)]
struct TomlTarget {
    name: String,
    crate_type: Option<Vec<String>>,
    path: Option<String>,
    test: Option<bool>
}

fn normalize(lib: Option<&[TomlLibTarget]>,
             bin: Option<&[TomlBinTarget]>) -> Vec<Target> {
    log!(4, "normalizing toml targets; lib={}; bin={}", lib, bin);

    fn target_profiles(target: &TomlTarget) -> Vec<Profile> {
        let mut ret = vec!(Profile::default("compile"));

        match target.test {
            Some(true) | None => ret.push(Profile::default("test").test(true)),
            _ => {}
        };

        ret
    }

    fn lib_targets(dst: &mut Vec<Target>, libs: &[TomlLibTarget]) {
        let l = &libs[0];
        let path = l.path.clone().unwrap_or_else(|| format!("src/{}.rs", l.name));
        let crate_types = l.crate_type.clone().and_then(|kinds| {
            LibKind::from_strs(kinds).ok()
        }).unwrap_or_else(|| vec!(Lib));

        for profile in target_profiles(l).iter() {
            dst.push(Target::lib_target(l.name.as_slice(), crate_types.clone(),
                                        &Path::new(path.as_slice()), profile));
        }
    }

    fn bin_targets(dst: &mut Vec<Target>, bins: &[TomlBinTarget],
                   default: |&TomlBinTarget| -> String) {
        for bin in bins.iter() {
            let path = bin.path.clone().unwrap_or_else(|| default(bin));

            for profile in target_profiles(bin).iter() {
                dst.push(Target::bin_target(bin.name.as_slice(),
                                            &Path::new(path.as_slice()), profile));
            }
        }
    }

    let mut ret = Vec::new();

    match (lib, bin) {
        (Some(ref libs), Some(ref bins)) => {
            lib_targets(&mut ret, libs.as_slice());
            bin_targets(&mut ret, bins.as_slice(),
                        |bin| format!("src/bin/{}.rs", bin.name));
        },
        (Some(ref libs), None) => {
            lib_targets(&mut ret, libs.as_slice());
        },
        (None, Some(ref bins)) => {
            bin_targets(&mut ret, bins.as_slice(),
                        |bin| format!("src/{}.rs", bin.name));
        },
        (None, None) => ()
    }

    ret
}
<|endoftext|>


<commit_before>use core::mem;
use core::intrinsics;
use pm;
use nvic;

use helpers::*;
use process::AppSlice;

/// Memory registers for a DMA channel. Section 16.6.1 of the datasheet
#[repr(C, packed)]
#[allow(dead_code)]
struct DMARegisters {
    memory_address:           usize,
    peripheral_select:        usize,
    transfer_counter:         usize,
    memory_address_reload:    usize,
    transfer_counter_reload:  usize,
    control:                  usize,
    mode:                     usize,
    status:                   usize,
    interrupt_enable:         usize,
    interrupt_disable:        usize,
    interrupt_mask:           usize,
    interrupt_status:         usize,
    version:                  usize,
    _unused:                  [usize; 3]
}

/// The PDCA's base addresses in memory (Section 7.1 of manual)
pub const DMA_BASE_ADDR : usize = 0x400A2000;

/// The number of bytes between each memory mapped DMA Channel (Section 16.6.1)
pub const DMA_CHANNEL_SIZE : usize = 0x40;

/// Shared counter that Keeps track of how many DMA channels are currently
/// active.
static mut NUM_ENABLED: usize = 0;

/// The DMA channel number. Each channel transfers data between memory and a
/// particular peripheral function (e.g., SPI read or SPI write, but not both
/// simultaneously). There are 16 available channels (Section 16.7)
#[derive(Copy,Clone)]
pub enum DMAChannelNum {
    // Relies on the fact that assigns values 0-15 to each constructor in order
    DMAChannel00 = 0,
    DMAChannel01 = 1,
    DMAChannel02 = 2,
    DMAChannel03 = 3,
    DMAChannel04 = 4,
    DMAChannel05 = 5,
    DMAChannel06 = 6,
    DMAChannel07 = 7,
    DMAChannel08 = 8,
    DMAChannel09 = 9,
    DMAChannel10 = 10,
    DMAChannel11 = 11,
    DMAChannel12 = 12,
    DMAChannel13 = 13,
    DMAChannel14 = 14,
    DMAChannel15 = 15
}

/// The peripheral function a channel is assigned to (Section 16.7)
/// *_RX means transfer data from peripheral to memory, *_TX means transfer data
/// from memory to peripheral.
#[allow(non_camel_case_types)]
pub enum DMAPeripheral {
    USART0_RX      = 0,
    USART1_RX      = 1,
    USART2_RX      = 2,
    USART3_RX      = 3,
    SPI_RX         = 4,
    TWIM0_RX       = 5,
    TWIM1_RX       = 6,
    TWIM2_RX       = 7,
    TWIM3_RX       = 8,
    TWIS0_RX       = 9,
    TWIS1_RX       = 10,
    ADCIFE_RX      = 11,
    CATB_RX        = 12,
    IISC_CH0_RX    = 14,
    IISC_CH1_RX    = 15,
    PARC_RX        = 16,
    AESA_RX        = 17,
    USART0_TX      = 18,
    USART1_TX      = 19,
    USART2_TX      = 20,
    USART3_TX      = 21,
    SPI_TX         = 22,
    TWIM0_TX       = 23,
    TWIM1_TX       = 24,
    TWIM2_TX       = 25,
    TWIM3_TX       = 26,
    TWIS0_TX       = 27,
    TWIS1_TX       = 28,
    ADCIFE_TX      = 29,
    CATB_TX        = 30,
    ABDACB_SDR0_TX = 31,
    ABDACB_SDR1_TX = 32,
    IISC_CH0_TX    = 33,
    IISC_CH1_TX    = 34,
    DACC_TX        = 35,
    AESA_TX        = 36,
    LCDCA_ACMDR_TX = 37,
    LCDCA_ABMDR_TX = 38
}

pub static mut DMAChannels : [DMAChannel; 16] = [
    DMAChannel::new(DMAChannelNum::DMAChannel00, nvic::NvicIdx::PDCA0),
    DMAChannel::new(DMAChannelNum::DMAChannel01, nvic::NvicIdx::PDCA1),
    DMAChannel::new(DMAChannelNum::DMAChannel02, nvic::NvicIdx::PDCA2),
    DMAChannel::new(DMAChannelNum::DMAChannel03, nvic::NvicIdx::PDCA3),
    DMAChannel::new(DMAChannelNum::DMAChannel04, nvic::NvicIdx::PDCA4),
    DMAChannel::new(DMAChannelNum::DMAChannel05, nvic::NvicIdx::PDCA5),
    DMAChannel::new(DMAChannelNum::DMAChannel06, nvic::NvicIdx::PDCA6),
    DMAChannel::new(DMAChannelNum::DMAChannel07, nvic::NvicIdx::PDCA7),
    DMAChannel::new(DMAChannelNum::DMAChannel08, nvic::NvicIdx::PDCA8),
    DMAChannel::new(DMAChannelNum::DMAChannel09, nvic::NvicIdx::PDCA9),
    DMAChannel::new(DMAChannelNum::DMAChannel10, nvic::NvicIdx::PDCA10),
    DMAChannel::new(DMAChannelNum::DMAChannel11, nvic::NvicIdx::PDCA11),
    DMAChannel::new(DMAChannelNum::DMAChannel12, nvic::NvicIdx::PDCA12),
    DMAChannel::new(DMAChannelNum::DMAChannel13, nvic::NvicIdx::PDCA13),
    DMAChannel::new(DMAChannelNum::DMAChannel14, nvic::NvicIdx::PDCA14),
    DMAChannel::new(DMAChannelNum::DMAChannel15, nvic::NvicIdx::PDCA15),
];

pub struct DMAChannel {
    registers: *mut DMARegisters,
    nvic: nvic::NvicIdx,
    pub client: Option<&'static mut DMAClient>,
    enabled: bool,
}

pub trait DMAClient {
    fn xfer_done(&mut self);
}

impl DMAChannel {
    const fn new(channel: DMAChannelNum, nvic: nvic::NvicIdx) -> DMAChannel {
        DMAChannel {
            registers: (DMA_BASE_ADDR + (channel as usize) * DMA_CHANNEL_SIZE)
                    as *mut DMARegisters,
            nvic: nvic,
            client: None,
            enabled: false
        }
    }

    pub fn enable(&mut self) {
        unsafe {
            pm::enable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
            pm::enable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
        }
        if !self.enabled {
            unsafe {
                let num_enabled = intrinsics::atomic_xadd(&mut NUM_ENABLED, 1);
                if num_enabled == 1 {
                    pm::enable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
                    pm::enable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
                }
            }
            let registers : &mut DMARegisters = unsafe {
                mem::transmute(self.registers)
            };
            volatile_store(&mut registers.control, 0x1);

            unsafe { nvic::enable(self.nvic) };

            self.enabled = true;
        }
    }

    pub fn disable(&mut self) {
        if self.enabled {
            unsafe {
                let num_enabled = intrinsics::atomic_xsub(&mut NUM_ENABLED, 1);
                if num_enabled == 1 {
                    pm::disable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
                    pm::disable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
                }
            }
            let registers : &mut DMARegisters = unsafe {
                mem::transmute(self.registers)
            };
            volatile_store(&mut registers.control, 0x2);
            self.enabled = false;
            unsafe {
                nvic::disable(self.nvic);
            }
        }
    }

    pub fn handle_interrupt(&mut self) {
        let registers : &mut DMARegisters = unsafe {
            mem::transmute(self.registers)
        };

        volatile_store(&mut registers.interrupt_disable, 1 << 1);
        self.client.as_mut().map(|client| {
            client.xfer_done();
        });
    }

    pub fn do_xfer<S>(&mut self, pid: usize, slice: AppSlice<S, u8>) {
        let registers : &mut DMARegisters = unsafe {
            mem::transmute(self.registers)
        };

        volatile_store(&mut registers.peripheral_select, pid);
        volatile_store(&mut registers.memory_address_reload,
                       &slice.as_ref()[0] as *const u8 as usize);
        volatile_store(&mut registers.transfer_counter_reload, slice.len());

        volatile_store(&mut registers.interrupt_enable, 1 << 1);
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub unsafe extern fn PDCA_0_Handler() {
    use common::Queue;
    use nvic;
    use chip;

    nvic::disable(nvic::NvicIdx::PDCA0);
    chip::INTERRUPT_QUEUE.as_mut().unwrap().enqueue(nvic::NvicIdx::PDCA0);
}

<commit_msg>Fix DMA interrupt handling<commit_after>use core::mem;
use core::intrinsics;
use pm;
use nvic;

use helpers::*;
use process::AppSlice;

/// Memory registers for a DMA channel. Section 16.6.1 of the datasheet
#[repr(C, packed)]
#[allow(dead_code)]
struct DMARegisters {
    memory_address:           usize,
    peripheral_select:        usize,
    transfer_counter:         usize,
    memory_address_reload:    usize,
    transfer_counter_reload:  usize,
    control:                  usize,
    mode:                     usize,
    status:                   usize,
    interrupt_enable:         usize,
    interrupt_disable:        usize,
    interrupt_mask:           usize,
    interrupt_status:         usize,
    version:                  usize,
    _unused:                  [usize; 3]
}

/// The PDCA's base addresses in memory (Section 7.1 of manual)
pub const DMA_BASE_ADDR : usize = 0x400A2000;

/// The number of bytes between each memory mapped DMA Channel (Section 16.6.1)
pub const DMA_CHANNEL_SIZE : usize = 0x40;

/// Shared counter that Keeps track of how many DMA channels are currently
/// active.
static mut NUM_ENABLED: usize = 0;

/// The DMA channel number. Each channel transfers data between memory and a
/// particular peripheral function (e.g., SPI read or SPI write, but not both
/// simultaneously). There are 16 available channels (Section 16.7)
#[derive(Copy,Clone)]
pub enum DMAChannelNum {
    // Relies on the fact that assigns values 0-15 to each constructor in order
    DMAChannel00 = 0,
    DMAChannel01 = 1,
    DMAChannel02 = 2,
    DMAChannel03 = 3,
    DMAChannel04 = 4,
    DMAChannel05 = 5,
    DMAChannel06 = 6,
    DMAChannel07 = 7,
    DMAChannel08 = 8,
    DMAChannel09 = 9,
    DMAChannel10 = 10,
    DMAChannel11 = 11,
    DMAChannel12 = 12,
    DMAChannel13 = 13,
    DMAChannel14 = 14,
    DMAChannel15 = 15
}

/// The peripheral function a channel is assigned to (Section 16.7)
/// *_RX means transfer data from peripheral to memory, *_TX means transfer data
/// from memory to peripheral.
#[allow(non_camel_case_types)]
pub enum DMAPeripheral {
    USART0_RX      = 0,
    USART1_RX      = 1,
    USART2_RX      = 2,
    USART3_RX      = 3,
    SPI_RX         = 4,
    TWIM0_RX       = 5,
    TWIM1_RX       = 6,
    TWIM2_RX       = 7,
    TWIM3_RX       = 8,
    TWIS0_RX       = 9,
    TWIS1_RX       = 10,
    ADCIFE_RX      = 11,
    CATB_RX        = 12,
    IISC_CH0_RX    = 14,
    IISC_CH1_RX    = 15,
    PARC_RX        = 16,
    AESA_RX        = 17,
    USART0_TX      = 18,
    USART1_TX      = 19,
    USART2_TX      = 20,
    USART3_TX      = 21,
    SPI_TX         = 22,
    TWIM0_TX       = 23,
    TWIM1_TX       = 24,
    TWIM2_TX       = 25,
    TWIM3_TX       = 26,
    TWIS0_TX       = 27,
    TWIS1_TX       = 28,
    ADCIFE_TX      = 29,
    CATB_TX        = 30,
    ABDACB_SDR0_TX = 31,
    ABDACB_SDR1_TX = 32,
    IISC_CH0_TX    = 33,
    IISC_CH1_TX    = 34,
    DACC_TX        = 35,
    AESA_TX        = 36,
    LCDCA_ACMDR_TX = 37,
    LCDCA_ABMDR_TX = 38
}

pub static mut DMAChannels : [DMAChannel; 16] = [
    DMAChannel::new(DMAChannelNum::DMAChannel00, nvic::NvicIdx::PDCA0),
    DMAChannel::new(DMAChannelNum::DMAChannel01, nvic::NvicIdx::PDCA1),
    DMAChannel::new(DMAChannelNum::DMAChannel02, nvic::NvicIdx::PDCA2),
    DMAChannel::new(DMAChannelNum::DMAChannel03, nvic::NvicIdx::PDCA3),
    DMAChannel::new(DMAChannelNum::DMAChannel04, nvic::NvicIdx::PDCA4),
    DMAChannel::new(DMAChannelNum::DMAChannel05, nvic::NvicIdx::PDCA5),
    DMAChannel::new(DMAChannelNum::DMAChannel06, nvic::NvicIdx::PDCA6),
    DMAChannel::new(DMAChannelNum::DMAChannel07, nvic::NvicIdx::PDCA7),
    DMAChannel::new(DMAChannelNum::DMAChannel08, nvic::NvicIdx::PDCA8),
    DMAChannel::new(DMAChannelNum::DMAChannel09, nvic::NvicIdx::PDCA9),
    DMAChannel::new(DMAChannelNum::DMAChannel10, nvic::NvicIdx::PDCA10),
    DMAChannel::new(DMAChannelNum::DMAChannel11, nvic::NvicIdx::PDCA11),
    DMAChannel::new(DMAChannelNum::DMAChannel12, nvic::NvicIdx::PDCA12),
    DMAChannel::new(DMAChannelNum::DMAChannel13, nvic::NvicIdx::PDCA13),
    DMAChannel::new(DMAChannelNum::DMAChannel14, nvic::NvicIdx::PDCA14),
    DMAChannel::new(DMAChannelNum::DMAChannel15, nvic::NvicIdx::PDCA15),
];

pub struct DMAChannel {
    registers: *mut DMARegisters,
    nvic: nvic::NvicIdx,
    pub client: Option<&'static mut DMAClient>,
    enabled: bool,
}

pub trait DMAClient {
    fn xfer_done(&mut self);
}

impl DMAChannel {
    const fn new(channel: DMAChannelNum, nvic: nvic::NvicIdx) -> DMAChannel {
        DMAChannel {
            registers: (DMA_BASE_ADDR + (channel as usize) * DMA_CHANNEL_SIZE)
                    as *mut DMARegisters,
            nvic: nvic,
            client: None,
            enabled: false
        }
    }

    pub fn enable(&mut self) {
        unsafe {
            pm::enable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
            pm::enable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
        }
        if !self.enabled {
            unsafe {
                let num_enabled = intrinsics::atomic_xadd(&mut NUM_ENABLED, 1);
                if num_enabled == 1 {
                    pm::enable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
                    pm::enable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
                }
            }
            let registers : &mut DMARegisters = unsafe {
                mem::transmute(self.registers)
            };
            volatile_store(&mut registers.interrupt_disable, 0xffffffff);
            volatile_store(&mut registers.control, 0x1);

            unsafe { nvic::enable(self.nvic) };

            self.enabled = true;
        }
    }

    pub fn disable(&mut self) {
        if self.enabled {
            unsafe {
                let num_enabled = intrinsics::atomic_xsub(&mut NUM_ENABLED, 1);
                if num_enabled == 1 {
                    pm::disable_clock(pm::Clock::HSB(pm::HSBClock::PDCA));
                    pm::disable_clock(pm::Clock::PBB(pm::PBBClock::PDCA));
                }
            }
            let registers : &mut DMARegisters = unsafe {
                mem::transmute(self.registers)
            };
            volatile_store(&mut registers.control, 0x2);
            self.enabled = false;
            unsafe {
                nvic::disable(self.nvic);
            }
        }
    }

    pub fn handle_interrupt(&mut self) {
        self.client.as_mut().map(|client| {
            client.xfer_done();
        });
    }

    pub fn do_xfer<S>(&mut self, pid: usize, slice: AppSlice<S, u8>) {
        let registers : &mut DMARegisters = unsafe {
            mem::transmute(self.registers)
        };

        volatile_store(&mut registers.peripheral_select, pid);
        volatile_store(&mut registers.memory_address_reload,
                       &slice.as_ref()[0] as *const u8 as usize);
        volatile_store(&mut registers.transfer_counter_reload, slice.len());

        volatile_store(&mut registers.interrupt_enable, 1 << 1);
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub unsafe extern fn PDCA_0_Handler() {
    use common::Queue;
    use nvic;
    use chip;

    let registers : &mut DMARegisters =
        mem::transmute(DMAChannels[0].registers);
    volatile_store(&mut registers.interrupt_disable, 0xffffffff);
    nvic::disable(nvic::NvicIdx::PDCA0);
    chip::INTERRUPT_QUEUE.as_mut().unwrap().enqueue(nvic::NvicIdx::PDCA0);
}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::result::Result as StdResult;

use tera::{Tera, Context as TeraContext};
use serde::ser::{SerializeStruct, self};

use config::Config;
use front_matter::{SectionFrontMatter, split_section_content};
use errors::{Result, ResultExt};
use utils::fs::{read_file, find_related_assets};
use utils::templates::render_template;
use utils::site::get_reading_analytics;
use rendering::{RenderContext, Header, render_content};

use page::Page;
use file_info::FileInfo;

#[derive(Clone, Debug, PartialEq)]
pub struct Section {
    /// All info about the actual file
    pub file: FileInfo,
    /// The front matter meta-data
    pub meta: SectionFrontMatter,
    /// The URL path of the page
    pub path: String,
    /// The components for the path of that page
    pub components: Vec<String>,
    /// The full URL for that page
    pub permalink: String,
    /// The actual content of the page, in markdown
    pub raw_content: String,
    /// The HTML rendered of the page
    pub content: String,
    /// All the non-md files we found next to the .md file
    pub assets: Vec<PathBuf>,
    /// All direct pages of that section
    pub pages: Vec<Page>,
    /// All pages that cannot be sorted in this section
    pub ignored_pages: Vec<Page>,
    /// All direct subsections
    pub subsections: Vec<Section>,
    /// Toc made from the headers of the markdown file
    pub toc: Vec<Header>,
}

impl Section {
    pub fn new<P: AsRef<Path>>(file_path: P, meta: SectionFrontMatter) -> Section {
        let file_path = file_path.as_ref();

        Section {
            file: FileInfo::new_section(file_path),
            meta,
            path: "".to_string(),
            components: vec![],
            permalink: "".to_string(),
            raw_content: "".to_string(),
            assets: vec![],
            content: "".to_string(),
            pages: vec![],
            ignored_pages: vec![],
            subsections: vec![],
            toc: vec![],
        }
    }

    pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Section> {
        let (meta, content) = split_section_content(file_path, content)?;
        let mut section = Section::new(file_path, meta);
        section.raw_content = content.clone();
        section.path = format!("{}/", section.file.components.join("/"));
        section.components = section.path.split('/')
            .map(|p| p.to_string())
            .filter(|p| !p.is_empty())
            .collect::<Vec<_>>();
        section.permalink = config.make_permalink(&section.path);
        Ok(section)
    }

    /// Read and parse a .md file into a Page struct
    pub fn from_file<P: AsRef<Path>>(path: P, config: &Config) -> Result<Section> {
        let path = path.as_ref();
        let content = read_file(path)?;
        let mut section = Section::parse(path, &content, config)?;

        if section.file.name == "_index" {
            let parent_dir = path.parent().unwrap();
            let assets = find_related_assets(parent_dir);

            if let Some(ref globset) = config.ignored_content_globset {
                // `find_related_assets` only scans the immediate directory (it is not recursive) so our
                // filtering only needs to work against the file_name component, not the full suffix. If
                // `find_related_assets` was changed to also return files in subdirectories, we could
                // use `PathBuf.strip_prefix` to remove the parent directory and then glob-filter
                // against the remaining path. Note that the current behaviour effectively means that
                // the `ignored_content` setting in the config file is limited to single-file glob
                // patterns (no "**" patterns).
                section.assets = assets.into_iter()
                    .filter(|path|
                        match path.file_name() {
                            None => true,
                            Some(file) => !globset.is_match(file)
                        }
                    ).collect();
            } else {
                section.assets = assets;
            }
        } else {
            section.assets = vec![];
        }

        Ok(section)
    }

    pub fn get_template_name(&self) -> String {
        match self.meta.template {
            Some(ref l) => l.to_string(),
            None => {
                if self.is_index() {
                    return "index.html".to_string();
                }
                "section.html".to_string()
            }
        }
    }

    /// We need access to all pages url to render links relative to content
    /// so that can't happen at the same time as parsing
    pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<()> {
        let mut context = RenderContext::new(
            tera,
            config,
            &self.permalink,
            permalinks,
            self.meta.insert_anchor_links,
        );

        context.tera_context.add("section", self);

        let res = render_content(&self.raw_content, &context)
            .chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?;
        self.content = res.0;
        self.toc = res.1;
        Ok(())
    }

    /// Renders the page using the default layout, unless specified in front-matter
    pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
        let tpl_name = self.get_template_name();

        let mut context = TeraContext::new();
        context.add("config", config);
        context.add("section", self);
        context.add("current_url", &self.permalink);
        context.add("current_path", &self.path);

        render_template(&tpl_name, tera, &context, &config.theme)
            .chain_err(|| format!("Failed to render section '{}'", self.file.path.display()))
    }

    /// Is this the index section?
    pub fn is_index(&self) -> bool {
        self.file.components.is_empty()
    }

    /// Returns all the paths of the pages belonging to that section
    pub fn all_pages_path(&self) -> Vec<PathBuf> {
        let mut paths = vec![];
        paths.extend(self.pages.iter().map(|p| p.file.path.clone()));
        paths.extend(self.ignored_pages.iter().map(|p| p.file.path.clone()));
        paths
    }

    /// Whether the page given belongs to that section
    pub fn is_child_page(&self, path: &PathBuf) -> bool {
        self.all_pages_path().contains(path)
    }

    /// Creates a vectors of asset URLs.
    fn serialize_assets(&self) -> Vec<String> {
        self.assets.iter()
            .filter_map(|asset| asset.file_name())
            .filter_map(|filename| filename.to_str())
            .map(|filename| self.path.clone() + filename)
            .collect()
    }
}

impl ser::Serialize for Section {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
        let mut state = serializer.serialize_struct("section", 13)?;
        state.serialize_field("content", &self.content)?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("title", &self.meta.title)?;
        state.serialize_field("description", &self.meta.description)?;
        state.serialize_field("extra", &self.meta.extra)?;
        state.serialize_field("path", &self.path)?;
        state.serialize_field("components", &self.components)?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("pages", &self.pages)?;
        state.serialize_field("subsections", &self.subsections)?;
        let (word_count, reading_time) = get_reading_analytics(&self.raw_content);
        state.serialize_field("word_count", &word_count)?;
        state.serialize_field("reading_time", &reading_time)?;
        state.serialize_field("toc", &self.toc)?;
        let assets = self.serialize_assets();
        state.serialize_field("assets", &assets)?;
        state.end()
    }
}

/// Used to create a default index section if there is no _index.md in the root content directory
impl Default for Section {
    fn default() -> Section {
        Section {
            file: FileInfo::default(),
            meta: SectionFrontMatter::default(),
            path: "".to_string(),
            components: vec![],
            permalink: "".to_string(),
            raw_content: "".to_string(),
            assets: vec![],
            content: "".to_string(),
            pages: vec![],
            ignored_pages: vec![],
            subsections: vec![],
            toc: vec![],
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io::Write;
    use std::fs::{File, create_dir};

    use tempfile::tempdir;
    use globset::{Glob, GlobSetBuilder};

    use config::Config;
    use super::Section;

    #[test]
    fn section_with_assets_gets_right_info() {
        let tmp_dir = tempdir().expect("create temp dir");
        let path = tmp_dir.path();
        create_dir(&path.join("content")).expect("create content temp dir");
        create_dir(&path.join("content").join("posts")).expect("create posts temp dir");
        let nested_path = path.join("content").join("posts").join("with-assets");
        create_dir(&nested_path).expect("create nested temp dir");
        let mut f = File::create(nested_path.join("_index.md")).unwrap();
        f.write_all(b"+++\n+++\n").unwrap();
        File::create(nested_path.join("example.js")).unwrap();
        File::create(nested_path.join("graph.jpg")).unwrap();
        File::create(nested_path.join("fail.png")).unwrap();

        let res = Section::from_file(
            nested_path.join("_index.md").as_path(),
            &Config::default(),
        );
        assert!(res.is_ok());
        let section = res.unwrap();
        assert_eq!(section.assets.len(), 3);
        assert_eq!(section.permalink, "http://a-website.com/posts/with-assets/");
    }

    #[test]
    fn section_with_ignored_assets_filters_out_correct_files() {
        let tmp_dir = tempdir().expect("create temp dir");
        let path = tmp_dir.path();
        create_dir(&path.join("content")).expect("create content temp dir");
        create_dir(&path.join("content").join("posts")).expect("create posts temp dir");
        let nested_path = path.join("content").join("posts").join("with-assets");
        create_dir(&nested_path).expect("create nested temp dir");
        let mut f = File::create(nested_path.join("_index.md")).unwrap();
        f.write_all(b"+++\nslug=\"hey\"\n+++\n").unwrap();
        File::create(nested_path.join("example.js")).unwrap();
        File::create(nested_path.join("graph.jpg")).unwrap();
        File::create(nested_path.join("fail.png")).unwrap();

        let mut gsb = GlobSetBuilder::new();
        gsb.add(Glob::new("*.{js,png}").unwrap());
        let mut config = Config::default();
        config.ignored_content_globset = Some(gsb.build().unwrap());

        let res = Section::from_file(
            nested_path.join("_index.md").as_path(),
            &config,
        );

        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.assets.len(), 1);
        assert_eq!(page.assets[0].file_name().unwrap().to_str(), Some("graph.jpg"));
    }
}
<commit_msg>Remove condition that's always true (optimization)<commit_after>use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::result::Result as StdResult;

use tera::{Tera, Context as TeraContext};
use serde::ser::{SerializeStruct, self};

use config::Config;
use front_matter::{SectionFrontMatter, split_section_content};
use errors::{Result, ResultExt};
use utils::fs::{read_file, find_related_assets};
use utils::templates::render_template;
use utils::site::get_reading_analytics;
use rendering::{RenderContext, Header, render_content};

use page::Page;
use file_info::FileInfo;

#[derive(Clone, Debug, PartialEq)]
pub struct Section {
    /// All info about the actual file
    pub file: FileInfo,
    /// The front matter meta-data
    pub meta: SectionFrontMatter,
    /// The URL path of the page
    pub path: String,
    /// The components for the path of that page
    pub components: Vec<String>,
    /// The full URL for that page
    pub permalink: String,
    /// The actual content of the page, in markdown
    pub raw_content: String,
    /// The HTML rendered of the page
    pub content: String,
    /// All the non-md files we found next to the .md file
    pub assets: Vec<PathBuf>,
    /// All direct pages of that section
    pub pages: Vec<Page>,
    /// All pages that cannot be sorted in this section
    pub ignored_pages: Vec<Page>,
    /// All direct subsections
    pub subsections: Vec<Section>,
    /// Toc made from the headers of the markdown file
    pub toc: Vec<Header>,
}

impl Section {
    pub fn new<P: AsRef<Path>>(file_path: P, meta: SectionFrontMatter) -> Section {
        let file_path = file_path.as_ref();

        Section {
            file: FileInfo::new_section(file_path),
            meta,
            path: "".to_string(),
            components: vec![],
            permalink: "".to_string(),
            raw_content: "".to_string(),
            assets: vec![],
            content: "".to_string(),
            pages: vec![],
            ignored_pages: vec![],
            subsections: vec![],
            toc: vec![],
        }
    }

    pub fn parse(file_path: &Path, content: &str, config: &Config) -> Result<Section> {
        let (meta, content) = split_section_content(file_path, content)?;
        let mut section = Section::new(file_path, meta);
        section.raw_content = content.clone();
        section.path = format!("{}/", section.file.components.join("/"));
        section.components = section.path.split('/')
            .map(|p| p.to_string())
            .filter(|p| !p.is_empty())
            .collect::<Vec<_>>();
        section.permalink = config.make_permalink(&section.path);
        Ok(section)
    }

    /// Read and parse a .md file into a Page struct
    pub fn from_file<P: AsRef<Path>>(path: P, config: &Config) -> Result<Section> {
        let path = path.as_ref();
        let content = read_file(path)?;
        let mut section = Section::parse(path, &content, config)?;

        let parent_dir = path.parent().unwrap();
        let assets = find_related_assets(parent_dir);

        if let Some(ref globset) = config.ignored_content_globset {
            // `find_related_assets` only scans the immediate directory (it is not recursive) so our
            // filtering only needs to work against the file_name component, not the full suffix. If
            // `find_related_assets` was changed to also return files in subdirectories, we could
            // use `PathBuf.strip_prefix` to remove the parent directory and then glob-filter
            // against the remaining path. Note that the current behaviour effectively means that
            // the `ignored_content` setting in the config file is limited to single-file glob
            // patterns (no "**" patterns).
            section.assets = assets.into_iter()
                .filter(|path|
                    match path.file_name() {
                        None => true,
                        Some(file) => !globset.is_match(file)
                    }
                ).collect();
        } else {
            section.assets = assets;
        }

        Ok(section)
    }

    pub fn get_template_name(&self) -> String {
        match self.meta.template {
            Some(ref l) => l.to_string(),
            None => {
                if self.is_index() {
                    return "index.html".to_string();
                }
                "section.html".to_string()
            }
        }
    }

    /// We need access to all pages url to render links relative to content
    /// so that can't happen at the same time as parsing
    pub fn render_markdown(&mut self, permalinks: &HashMap<String, String>, tera: &Tera, config: &Config) -> Result<()> {
        let mut context = RenderContext::new(
            tera,
            config,
            &self.permalink,
            permalinks,
            self.meta.insert_anchor_links,
        );

        context.tera_context.add("section", self);

        let res = render_content(&self.raw_content, &context)
            .chain_err(|| format!("Failed to render content of {}", self.file.path.display()))?;
        self.content = res.0;
        self.toc = res.1;
        Ok(())
    }

    /// Renders the page using the default layout, unless specified in front-matter
    pub fn render_html(&self, tera: &Tera, config: &Config) -> Result<String> {
        let tpl_name = self.get_template_name();

        let mut context = TeraContext::new();
        context.add("config", config);
        context.add("section", self);
        context.add("current_url", &self.permalink);
        context.add("current_path", &self.path);

        render_template(&tpl_name, tera, &context, &config.theme)
            .chain_err(|| format!("Failed to render section '{}'", self.file.path.display()))
    }

    /// Is this the index section?
    pub fn is_index(&self) -> bool {
        self.file.components.is_empty()
    }

    /// Returns all the paths of the pages belonging to that section
    pub fn all_pages_path(&self) -> Vec<PathBuf> {
        let mut paths = vec![];
        paths.extend(self.pages.iter().map(|p| p.file.path.clone()));
        paths.extend(self.ignored_pages.iter().map(|p| p.file.path.clone()));
        paths
    }

    /// Whether the page given belongs to that section
    pub fn is_child_page(&self, path: &PathBuf) -> bool {
        self.all_pages_path().contains(path)
    }

    /// Creates a vectors of asset URLs.
    fn serialize_assets(&self) -> Vec<String> {
        self.assets.iter()
            .filter_map(|asset| asset.file_name())
            .filter_map(|filename| filename.to_str())
            .map(|filename| self.path.clone() + filename)
            .collect()
    }
}

impl ser::Serialize for Section {
    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error> where S: ser::Serializer {
        let mut state = serializer.serialize_struct("section", 13)?;
        state.serialize_field("content", &self.content)?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("title", &self.meta.title)?;
        state.serialize_field("description", &self.meta.description)?;
        state.serialize_field("extra", &self.meta.extra)?;
        state.serialize_field("path", &self.path)?;
        state.serialize_field("components", &self.components)?;
        state.serialize_field("permalink", &self.permalink)?;
        state.serialize_field("pages", &self.pages)?;
        state.serialize_field("subsections", &self.subsections)?;
        let (word_count, reading_time) = get_reading_analytics(&self.raw_content);
        state.serialize_field("word_count", &word_count)?;
        state.serialize_field("reading_time", &reading_time)?;
        state.serialize_field("toc", &self.toc)?;
        let assets = self.serialize_assets();
        state.serialize_field("assets", &assets)?;
        state.end()
    }
}

/// Used to create a default index section if there is no _index.md in the root content directory
impl Default for Section {
    fn default() -> Section {
        Section {
            file: FileInfo::default(),
            meta: SectionFrontMatter::default(),
            path: "".to_string(),
            components: vec![],
            permalink: "".to_string(),
            raw_content: "".to_string(),
            assets: vec![],
            content: "".to_string(),
            pages: vec![],
            ignored_pages: vec![],
            subsections: vec![],
            toc: vec![],
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io::Write;
    use std::fs::{File, create_dir};

    use tempfile::tempdir;
    use globset::{Glob, GlobSetBuilder};

    use config::Config;
    use super::Section;

    #[test]
    fn section_with_assets_gets_right_info() {
        let tmp_dir = tempdir().expect("create temp dir");
        let path = tmp_dir.path();
        create_dir(&path.join("content")).expect("create content temp dir");
        create_dir(&path.join("content").join("posts")).expect("create posts temp dir");
        let nested_path = path.join("content").join("posts").join("with-assets");
        create_dir(&nested_path).expect("create nested temp dir");
        let mut f = File::create(nested_path.join("_index.md")).unwrap();
        f.write_all(b"+++\n+++\n").unwrap();
        File::create(nested_path.join("example.js")).unwrap();
        File::create(nested_path.join("graph.jpg")).unwrap();
        File::create(nested_path.join("fail.png")).unwrap();

        let res = Section::from_file(
            nested_path.join("_index.md").as_path(),
            &Config::default(),
        );
        assert!(res.is_ok());
        let section = res.unwrap();
        assert_eq!(section.assets.len(), 3);
        assert_eq!(section.permalink, "http://a-website.com/posts/with-assets/");
    }

    #[test]
    fn section_with_ignored_assets_filters_out_correct_files() {
        let tmp_dir = tempdir().expect("create temp dir");
        let path = tmp_dir.path();
        create_dir(&path.join("content")).expect("create content temp dir");
        create_dir(&path.join("content").join("posts")).expect("create posts temp dir");
        let nested_path = path.join("content").join("posts").join("with-assets");
        create_dir(&nested_path).expect("create nested temp dir");
        let mut f = File::create(nested_path.join("_index.md")).unwrap();
        f.write_all(b"+++\nslug=\"hey\"\n+++\n").unwrap();
        File::create(nested_path.join("example.js")).unwrap();
        File::create(nested_path.join("graph.jpg")).unwrap();
        File::create(nested_path.join("fail.png")).unwrap();

        let mut gsb = GlobSetBuilder::new();
        gsb.add(Glob::new("*.{js,png}").unwrap());
        let mut config = Config::default();
        config.ignored_content_globset = Some(gsb.build().unwrap());

        let res = Section::from_file(
            nested_path.join("_index.md").as_path(),
            &config,
        );

        assert!(res.is_ok());
        let page = res.unwrap();
        assert_eq!(page.assets.len(), 1);
        assert_eq!(page.assets[0].file_name().unwrap().to_str(), Some("graph.jpg"));
    }
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};

use slotmap::{DenseSlotMap, Key};

use front_matter::SortBy;

use sorting::{find_siblings, sort_pages_by_weight, sort_pages_by_date};
use content::{Page, Section};

/// Houses everything about pages and sections
/// Think of it as a database where each page and section has an id (Key here)
/// that can be used to find the actual value
/// Sections and pages can then refer to other elements by those keys, which are very cheap to
/// copy.
/// We can assume the keys are always existing as removing a page/section deletes all references
/// to that key.
#[derive(Debug)]
pub struct Library {
    /// All the pages of the site
    pages: DenseSlotMap<Page>,
    /// All the sections of the site
    sections: DenseSlotMap<Section>,
    /// A mapping path -> key for pages so we can easily get their key
    paths_to_pages: HashMap<PathBuf, Key>,
    /// A mapping path -> key for sections so we can easily get their key
    pub paths_to_sections: HashMap<PathBuf, Key>,
}

impl Library {
    pub fn new(cap_pages: usize, cap_sections: usize) -> Self {
        Library {
            pages: DenseSlotMap::with_capacity(cap_pages),
            sections: DenseSlotMap::with_capacity(cap_sections),
            paths_to_pages: HashMap::with_capacity(cap_pages),
            paths_to_sections: HashMap::with_capacity(cap_sections),
        }
    }

    /// Add a section and return its Key
    pub fn insert_section(&mut self, section: Section) -> Key {
        let path = section.file.path.clone();
        let key = self.sections.insert(section);
        self.paths_to_sections.insert(path, key);
        key
    }

    /// Add a page and return its Key
    pub fn insert_page(&mut self, page: Page) -> Key {
        let path = page.file.path.clone();
        let key = self.pages.insert(page);
        self.paths_to_pages.insert(path, key);
        key
    }

    pub fn pages(&self) -> &DenseSlotMap<Page> {
        &self.pages
    }

    pub fn pages_mut(&mut self) -> &mut DenseSlotMap<Page> {
        &mut self.pages
    }

    pub fn pages_values(&self) -> Vec<&Page> {
        self.pages.values().collect::<Vec<_>>()
    }

    pub fn sections(&self) -> &DenseSlotMap<Section> {
        &self.sections
    }

    pub fn sections_mut(&mut self) -> &mut DenseSlotMap<Section> {
        &mut self.sections
    }

    pub fn sections_values(&self) -> Vec<&Section> {
        self.sections.values().collect::<Vec<_>>()
    }

    /// Find out the direct subsections of each subsection if there are some
    /// as well as the pages for each section
    pub fn populate_sections(&mut self) {
        let (root_path, index_path) = self.sections
            .values()
            .find(|s| s.is_index())
            .map(|s| (s.file.parent.clone(), s.file.path.clone()))
            .unwrap();
        let root_key =  self.paths_to_sections[&index_path];

        // We are going to get both the ancestors and grandparents for each section in one go
        let mut ancestors: HashMap<PathBuf, Vec<_>> = HashMap::new();
        let mut subsections: HashMap<PathBuf, Vec<_>> = HashMap::new();

        for section in self.sections.values_mut() {
            // Make sure the pages of a section are empty since we can call that many times on `serve`
            section.pages = vec![];
            section.ignored_pages = vec![];

            if let Some(ref grand_parent) = section.file.grand_parent {
                subsections
                    .entry(grand_parent.join("_index.md"))
                    .or_insert_with(|| vec![])
                    .push(section.file.path.clone());
            }

            // Index has no ancestors, no need to go through it
            if section.is_index() {
                ancestors.insert(section.file.path.clone(), vec![]);
                continue;
            }

            let mut path = root_path.clone();
            // Index section is the first ancestor of every single section
            let mut parents = vec![root_key.clone()];
            for component in &section.file.components {
                path = path.join(component);
                // Skip itself
                if path == section.file.parent {
                    continue;
                }
                if let Some(section_key) = self.paths_to_sections.get(&path.join("_index.md")) {
                    parents.push(*section_key);
                }
            }
            ancestors.insert(section.file.path.clone(), parents);
        }

        for (key, page) in &mut self.pages {
            let parent_section_path = page.file.parent.join("_index.md");
            if let Some(section_key) = self.paths_to_sections.get(&parent_section_path) {
                self.sections.get_mut(*section_key).unwrap().pages.push(key);
                page.ancestors = ancestors.get(&parent_section_path).cloned().unwrap_or_else(|| vec![]);
                // Don't forget to push the actual parent
                page.ancestors.push(*section_key);
            }
        }

        self.sort_sections_pages();

        let sections = self.paths_to_sections.clone();
        let mut sections_weight = HashMap::new();
        for (key, section) in &self.sections {
            sections_weight.insert(key, section.meta.weight);
        }

        for section in self.sections.values_mut() {
            if let Some(ref children) = subsections.get(&section.file.path) {
                let mut children: Vec<_> = children.iter().map(|p| sections[p]).collect();
                children.sort_by(|a, b| sections_weight[a].cmp(&sections_weight[b]));
                section.subsections = children;
            }
            section.ancestors = ancestors.get(&section.file.path).cloned().unwrap_or_else(|| vec![]);
        }
    }

    /// Sort all sections pages
    pub fn sort_sections_pages(&mut self) {
        let mut updates = HashMap::new();
        for (key, section) in &self.sections {
            let (sorted_pages, cannot_be_sorted_pages) = match section.meta.sort_by {
                SortBy::None => continue,
                SortBy::Date => {
                    let data = section.pages
                        .iter()
                        .map(|k| {
                            if let Some(page) = self.pages.get(*k) {
                                (k, page.meta.datetime, page.permalink.as_ref())
                            } else {
                                unreachable!("Sorting got an unknown page")
                            }
                        })
                        .collect();

                    sort_pages_by_date(data)
                },
                SortBy::Weight => {
                    let data = section.pages
                        .iter()
                        .map(|k| {
                            if let Some(page) = self.pages.get(*k) {
                                (k, page.meta.weight, page.permalink.as_ref())
                            } else {
                                unreachable!("Sorting got an unknown page")
                            }
                        })
                        .collect();

                    sort_pages_by_weight(data)
                }
            };
            updates.insert(key, (sorted_pages, cannot_be_sorted_pages, section.meta.sort_by));
        }

        for (key, (sorted, cannot_be_sorted, sort_by)) in updates {
            // Find sibling between sorted pages first
            let with_siblings = find_siblings(sorted.iter().map(|k| {
                if let Some(page) = self.pages.get(*k) {
                    (k, page.is_draft())
                } else {
                    unreachable!("Sorting got an unknown page")
                }
            }).collect());

            for (k2, val1, val2) in with_siblings {
                if let Some(page) = self.pages.get_mut(k2) {
                    match sort_by {
                        SortBy::Date => {
                            page.earlier = val2;
                            page.later = val1;
                        },
                        SortBy::Weight => {
                            page.lighter = val1;
                            page.heavier = val2;
                        },
                        SortBy::None => unreachable!("Impossible to find siblings in SortBy::None")
                    }
                } else {
                    unreachable!("Sorting got an unknown page")
                }
            }

            if let Some(s) = self.sections.get_mut(key) {
                s.pages = sorted;
                s.ignored_pages = cannot_be_sorted;
            }
        }
    }

    /// Find all the orphan pages: pages that are in a folder without an `_index.md`
    pub fn get_all_orphan_pages(&self) -> Vec<&Page> {
        let pages_in_sections = self.sections
            .values()
            .flat_map(|s| &s.pages)
            .collect::<HashSet<_>>();

        self.pages
            .iter()
            .filter(|(key, _)| !pages_in_sections.contains(&key))
            .map(|(_, page)| page)
            .collect()
    }

    pub fn find_parent_section(&self, path: &Path) -> Option<&Section> {
        let page_key = self.paths_to_pages[path];
        for s in self.sections.values() {
            if s.pages.contains(&page_key) {
                return Some(s)
            }
        }

        None
    }

    /// Only used in tests
    pub fn get_section_key(&self, path: &PathBuf) -> Option<&Key> {
        self.paths_to_sections.get(path)
    }

    pub fn get_section(&self, path: &PathBuf) -> Option<&Section> {
        self.sections.get(self.paths_to_sections.get(path).cloned().unwrap_or_default())
    }

    pub fn get_section_mut(&mut self, path: &PathBuf) -> Option<&mut Section> {
        self.sections.get_mut(self.paths_to_sections.get(path).cloned().unwrap_or_default())
    }

    pub fn get_section_by_key(&self, key: Key) -> &Section {
        self.sections.get(key).unwrap()
    }

    pub fn get_section_mut_by_key(&mut self, key: Key) -> &mut Section {
        self.sections.get_mut(key).unwrap()
    }

    pub fn get_section_path_by_key(&self, key: Key) -> &str {
        &self.get_section_by_key(key).file.relative
    }

    pub fn get_page(&self, path: &PathBuf) -> Option<&Page> {
        self.pages.get(self.paths_to_pages.get(path).cloned().unwrap_or_default())
    }

    pub fn get_page_by_key(&self, key: Key) -> &Page {
        self.pages.get(key).unwrap()
    }

    pub fn remove_section(&mut self, path: &PathBuf) -> Option<Section> {
        if let Some(k) = self.paths_to_sections.remove(path) {
            self.sections.remove(k)
        } else {
            None
        }
    }

    pub fn remove_page(&mut self, path: &PathBuf) -> Option<Page> {
        if let Some(k) = self.paths_to_pages.remove(path) {
            self.pages.remove(k)
        } else {
            None
        }
    }

    /// Used in rebuild, to check if we know it already
    pub fn contains_section(&self, path: &PathBuf) -> bool {
        self.paths_to_sections.contains_key(path)
    }

    /// Used in rebuild, to check if we know it already
    pub fn contains_page(&self, path: &PathBuf) -> bool {
        self.paths_to_pages.contains_key(path)
    }
}
<commit_msg>No need for clone<commit_after>use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};

use slotmap::{DenseSlotMap, Key};

use front_matter::SortBy;

use sorting::{find_siblings, sort_pages_by_weight, sort_pages_by_date};
use content::{Page, Section};

/// Houses everything about pages and sections
/// Think of it as a database where each page and section has an id (Key here)
/// that can be used to find the actual value
/// Sections and pages can then refer to other elements by those keys, which are very cheap to
/// copy.
/// We can assume the keys are always existing as removing a page/section deletes all references
/// to that key.
#[derive(Debug)]
pub struct Library {
    /// All the pages of the site
    pages: DenseSlotMap<Page>,
    /// All the sections of the site
    sections: DenseSlotMap<Section>,
    /// A mapping path -> key for pages so we can easily get their key
    paths_to_pages: HashMap<PathBuf, Key>,
    /// A mapping path -> key for sections so we can easily get their key
    pub paths_to_sections: HashMap<PathBuf, Key>,
}

impl Library {
    pub fn new(cap_pages: usize, cap_sections: usize) -> Self {
        Library {
            pages: DenseSlotMap::with_capacity(cap_pages),
            sections: DenseSlotMap::with_capacity(cap_sections),
            paths_to_pages: HashMap::with_capacity(cap_pages),
            paths_to_sections: HashMap::with_capacity(cap_sections),
        }
    }

    /// Add a section and return its Key
    pub fn insert_section(&mut self, section: Section) -> Key {
        let path = section.file.path.clone();
        let key = self.sections.insert(section);
        self.paths_to_sections.insert(path, key);
        key
    }

    /// Add a page and return its Key
    pub fn insert_page(&mut self, page: Page) -> Key {
        let path = page.file.path.clone();
        let key = self.pages.insert(page);
        self.paths_to_pages.insert(path, key);
        key
    }

    pub fn pages(&self) -> &DenseSlotMap<Page> {
        &self.pages
    }

    pub fn pages_mut(&mut self) -> &mut DenseSlotMap<Page> {
        &mut self.pages
    }

    pub fn pages_values(&self) -> Vec<&Page> {
        self.pages.values().collect::<Vec<_>>()
    }

    pub fn sections(&self) -> &DenseSlotMap<Section> {
        &self.sections
    }

    pub fn sections_mut(&mut self) -> &mut DenseSlotMap<Section> {
        &mut self.sections
    }

    pub fn sections_values(&self) -> Vec<&Section> {
        self.sections.values().collect::<Vec<_>>()
    }

    /// Find out the direct subsections of each subsection if there are some
    /// as well as the pages for each section
    pub fn populate_sections(&mut self) {
        let (root_path, index_path) = self.sections
            .values()
            .find(|s| s.is_index())
            .map(|s| (s.file.parent.clone(), s.file.path.clone()))
            .unwrap();
        let root_key =  self.paths_to_sections[&index_path];

        // We are going to get both the ancestors and grandparents for each section in one go
        let mut ancestors: HashMap<PathBuf, Vec<_>> = HashMap::new();
        let mut subsections: HashMap<PathBuf, Vec<_>> = HashMap::new();

        for section in self.sections.values_mut() {
            // Make sure the pages of a section are empty since we can call that many times on `serve`
            section.pages = vec![];
            section.ignored_pages = vec![];

            if let Some(ref grand_parent) = section.file.grand_parent {
                subsections
                    .entry(grand_parent.join("_index.md"))
                    .or_insert_with(|| vec![])
                    .push(section.file.path.clone());
            }

            // Index has no ancestors, no need to go through it
            if section.is_index() {
                ancestors.insert(section.file.path.clone(), vec![]);
                continue;
            }

            let mut path = root_path.clone();
            // Index section is the first ancestor of every single section
            let mut parents = vec![root_key];
            for component in &section.file.components {
                path = path.join(component);
                // Skip itself
                if path == section.file.parent {
                    continue;
                }
                if let Some(section_key) = self.paths_to_sections.get(&path.join("_index.md")) {
                    parents.push(*section_key);
                }
            }
            ancestors.insert(section.file.path.clone(), parents);
        }

        for (key, page) in &mut self.pages {
            let parent_section_path = page.file.parent.join("_index.md");
            if let Some(section_key) = self.paths_to_sections.get(&parent_section_path) {
                self.sections.get_mut(*section_key).unwrap().pages.push(key);
                page.ancestors = ancestors.get(&parent_section_path).cloned().unwrap_or_else(|| vec![]);
                // Don't forget to push the actual parent
                page.ancestors.push(*section_key);
            }
        }

        self.sort_sections_pages();

        let sections = self.paths_to_sections.clone();
        let mut sections_weight = HashMap::new();
        for (key, section) in &self.sections {
            sections_weight.insert(key, section.meta.weight);
        }

        for section in self.sections.values_mut() {
            if let Some(ref children) = subsections.get(&section.file.path) {
                let mut children: Vec<_> = children.iter().map(|p| sections[p]).collect();
                children.sort_by(|a, b| sections_weight[a].cmp(&sections_weight[b]));
                section.subsections = children;
            }
            section.ancestors = ancestors.get(&section.file.path).cloned().unwrap_or_else(|| vec![]);
        }
    }

    /// Sort all sections pages
    pub fn sort_sections_pages(&mut self) {
        let mut updates = HashMap::new();
        for (key, section) in &self.sections {
            let (sorted_pages, cannot_be_sorted_pages) = match section.meta.sort_by {
                SortBy::None => continue,
                SortBy::Date => {
                    let data = section.pages
                        .iter()
                        .map(|k| {
                            if let Some(page) = self.pages.get(*k) {
                                (k, page.meta.datetime, page.permalink.as_ref())
                            } else {
                                unreachable!("Sorting got an unknown page")
                            }
                        })
                        .collect();

                    sort_pages_by_date(data)
                },
                SortBy::Weight => {
                    let data = section.pages
                        .iter()
                        .map(|k| {
                            if let Some(page) = self.pages.get(*k) {
                                (k, page.meta.weight, page.permalink.as_ref())
                            } else {
                                unreachable!("Sorting got an unknown page")
                            }
                        })
                        .collect();

                    sort_pages_by_weight(data)
                }
            };
            updates.insert(key, (sorted_pages, cannot_be_sorted_pages, section.meta.sort_by));
        }

        for (key, (sorted, cannot_be_sorted, sort_by)) in updates {
            // Find sibling between sorted pages first
            let with_siblings = find_siblings(sorted.iter().map(|k| {
                if let Some(page) = self.pages.get(*k) {
                    (k, page.is_draft())
                } else {
                    unreachable!("Sorting got an unknown page")
                }
            }).collect());

            for (k2, val1, val2) in with_siblings {
                if let Some(page) = self.pages.get_mut(k2) {
                    match sort_by {
                        SortBy::Date => {
                            page.earlier = val2;
                            page.later = val1;
                        },
                        SortBy::Weight => {
                            page.lighter = val1;
                            page.heavier = val2;
                        },
                        SortBy::None => unreachable!("Impossible to find siblings in SortBy::None")
                    }
                } else {
                    unreachable!("Sorting got an unknown page")
                }
            }

            if let Some(s) = self.sections.get_mut(key) {
                s.pages = sorted;
                s.ignored_pages = cannot_be_sorted;
            }
        }
    }

    /// Find all the orphan pages: pages that are in a folder without an `_index.md`
    pub fn get_all_orphan_pages(&self) -> Vec<&Page> {
        let pages_in_sections = self.sections
            .values()
            .flat_map(|s| &s.pages)
            .collect::<HashSet<_>>();

        self.pages
            .iter()
            .filter(|(key, _)| !pages_in_sections.contains(&key))
            .map(|(_, page)| page)
            .collect()
    }

    pub fn find_parent_section(&self, path: &Path) -> Option<&Section> {
        let page_key = self.paths_to_pages[path];
        for s in self.sections.values() {
            if s.pages.contains(&page_key) {
                return Some(s)
            }
        }

        None
    }

    /// Only used in tests
    pub fn get_section_key(&self, path: &PathBuf) -> Option<&Key> {
        self.paths_to_sections.get(path)
    }

    pub fn get_section(&self, path: &PathBuf) -> Option<&Section> {
        self.sections.get(self.paths_to_sections.get(path).cloned().unwrap_or_default())
    }

    pub fn get_section_mut(&mut self, path: &PathBuf) -> Option<&mut Section> {
        self.sections.get_mut(self.paths_to_sections.get(path).cloned().unwrap_or_default())
    }

    pub fn get_section_by_key(&self, key: Key) -> &Section {
        self.sections.get(key).unwrap()
    }

    pub fn get_section_mut_by_key(&mut self, key: Key) -> &mut Section {
        self.sections.get_mut(key).unwrap()
    }

    pub fn get_section_path_by_key(&self, key: Key) -> &str {
        &self.get_section_by_key(key).file.relative
    }

    pub fn get_page(&self, path: &PathBuf) -> Option<&Page> {
        self.pages.get(self.paths_to_pages.get(path).cloned().unwrap_or_default())
    }

    pub fn get_page_by_key(&self, key: Key) -> &Page {
        self.pages.get(key).unwrap()
    }

    pub fn remove_section(&mut self, path: &PathBuf) -> Option<Section> {
        if let Some(k) = self.paths_to_sections.remove(path) {
            self.sections.remove(k)
        } else {
            None
        }
    }

    pub fn remove_page(&mut self, path: &PathBuf) -> Option<Page> {
        if let Some(k) = self.paths_to_pages.remove(path) {
            self.pages.remove(k)
        } else {
            None
        }
    }

    /// Used in rebuild, to check if we know it already
    pub fn contains_section(&self, path: &PathBuf) -> bool {
        self.paths_to_sections.contains_key(path)
    }

    /// Used in rebuild, to check if we know it already
    pub fn contains_page(&self, path: &PathBuf) -> bool {
        self.paths_to_pages.contains_key(path)
    }
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate fruently;

#[cfg(test)]
mod tests {
    use test::Bencher;
    use fruently::fluent::Fluent;
    use std::collections::HashMap;

    #[bench]
    fn benchmark_json_forwardable(b: &mut Bencher) {
        use fruently::forwardable::JsonForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }

    #[bench]
    fn benchmark_msgpack_forwardable(b: &mut Bencher) {
        use fruently::forwardable::MsgpackForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }
}
<commit_msg>bench: Add forward mode bench test case<commit_after>#![feature(test)]

extern crate test;
extern crate time;
extern crate fruently;

#[cfg(test)]
mod tests {
    use test;
    use test::Bencher;
    use time;
    use fruently::fluent::Fluent;
    use std::collections::HashMap;

    #[bench]
    fn benchmark_json_forwardable(b: &mut Bencher) {
        use fruently::forwardable::JsonForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }

    #[bench]
    fn benchmark_msgpack_forwardable(b: &mut Bencher) {
        use fruently::forwardable::MsgpackForwardable;

        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        b.iter(|| {
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(&obj);
        });
    }

    #[bench]
    fn benchmark_forwardable(b: &mut Bencher) {
        use fruently::forwardable::Forwardable;

        let mut obj = HashMap::new();
        let time = time::now().to_timespec().sec;
        b.iter(|| {
            let n = test::black_box(1000);
            let hmap = (0..n).fold(&mut obj, |mut acc, _| {
                {
                    acc.insert("fwd", "fruently".to_string());
                }
                acc
            });
            let entry = (time, &*hmap);
            let fruently = Fluent::new("0.0.0.0:24224", "test");
            let _ = fruently.post(vec![(entry)]);
        })
    }
}
<|endoftext|>


<commit_before>use ast::{Expression, Module};
use compileerror::{CompileResult};

struct MarkTailCallContext
{
    function_stack: Vec<String>
}

impl MarkTailCallContext
{
    pub fn new() -> MarkTailCallContext
    {
        MarkTailCallContext{
            function_stack: Vec::new()
        }
    }

    pub fn push(&mut self, name: &str)
    {
        self.function_stack.push(name.into())
    }

    pub fn pop(&mut self)
    {
        self.function_stack.pop().expect("Stack is empty");
    }

    pub fn is_recursive_call(&self, name: &str) -> bool
    {
        self.function_stack.last().map(|f| f == name).unwrap_or(false)
    }
}

fn mark_tail_call(ctx: &mut MarkTailCallContext, e: &mut Expression) -> CompileResult<()>
{
    match *e
    {
        Expression::Call(ref mut c) => {
            if ctx.is_recursive_call(&c.callee.name) {
                c.tail_call = true;
            }
            Ok(())
        },

        Expression::UnaryOp(ref mut op) => mark_tail_call(ctx, &mut op.expression),
        Expression::BinaryOp(ref mut op) => mark_tail_call(ctx, &mut op.right),

        Expression::Function(ref mut f) => {
            ctx.push(&f.sig.name);
            try!(mark_tail_call(ctx, &mut f.expression));
            ctx.pop();
            Ok(())
        },

        Expression::Match(ref mut m) => {
            let mut mc = m.cases.last_mut().expect("No cases in match statement");
            mark_tail_call(ctx, &mut mc.to_execute)
        },

        Expression::Enclosed(_, ref mut inner) => mark_tail_call(ctx, inner),
        Expression::Let(ref mut l) => mark_tail_call(ctx, &mut l.expression),
        _ => Ok(())
    }
}

/*
    Type check and infer all the unkown types
*/
pub fn mark_tail_calls(module: &mut Module) -> CompileResult<()>
{
    let mut ctx = MarkTailCallContext::new();
    for ref mut e in module.expressions.iter_mut()
    {
        try!(mark_tail_call(&mut ctx, e));
    }

    Ok(())
}
<commit_msg>Add some more comments<commit_after>use ast::{Expression, Module};
use compileerror::{CompileResult};

struct MarkTailCallContext
{
    function_stack: Vec<String>
}

impl MarkTailCallContext
{
    pub fn new() -> MarkTailCallContext
    {
        MarkTailCallContext{
            function_stack: Vec::new()
        }
    }

    pub fn push(&mut self, name: &str)
    {
        self.function_stack.push(name.into())
    }

    pub fn pop(&mut self)
    {
        self.function_stack.pop().expect("Stack is empty");
    }

    pub fn is_recursive_call(&self, name: &str) -> bool
    {
        self.function_stack.last().map(|f| f == name).unwrap_or(false)
    }
}

fn mark_tail_call(ctx: &mut MarkTailCallContext, e: &mut Expression) -> CompileResult<()>
{
    match *e
    {
        Expression::Call(ref mut c) => {
            // We only get here if this is a tail call, so only check if it is recursive
            if ctx.is_recursive_call(&c.callee.name) {
                c.tail_call = true;
            }
            Ok(())
        },

        Expression::UnaryOp(ref mut op) => mark_tail_call(ctx, &mut op.expression),
        Expression::BinaryOp(ref mut op) => mark_tail_call(ctx, &mut op.right), // Only right can be a tail call

        Expression::Function(ref mut f) => {
            ctx.push(&f.sig.name);
            try!(mark_tail_call(ctx, &mut f.expression));
            ctx.pop();
            Ok(())
        },

        Expression::Match(ref mut m) => {
            let mut mc = m.cases.last_mut().expect("No cases in match statement"); // Only the last case can have a tail call
            mark_tail_call(ctx, &mut mc.to_execute)
        },

        Expression::Enclosed(_, ref mut inner) => mark_tail_call(ctx, inner),
        Expression::Let(ref mut l) => mark_tail_call(ctx, &mut l.expression),
        _ => Ok(())
    }
}

/*
    Mark calls as tail calls
*/
pub fn mark_tail_calls(module: &mut Module) -> CompileResult<()>
{
    let mut ctx = MarkTailCallContext::new();
    for ref mut e in module.expressions.iter_mut()
    {
        try!(mark_tail_call(&mut ctx, e));
    }

    Ok(())
}
<|endoftext|>


<commit_before>use rustc::hir::*;
use rustc::lint::*;
use syntax::ast::{Name, NodeId};
use syntax::codemap::Span;
use syntax::parse::token::InternedString;
use utils::span_lint;

/// **What it does:** This lint checks for imports that remove "unsafe" from an item's name
///
/// **Why is this bad?** Renaming makes it less clear which traits and structures are unsafe.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// use std::cell::{UnsafeCell as TotallySafeCell};
///
/// extern crate crossbeam;
/// use crossbeam::{spawn_unsafe as spawn};
/// ```
declare_lint! {
    pub UNSAFE_REMOVED_FROM_NAME,
    Warn,
    "unsafe removed from name"
}

pub struct UnsafeNameRemoval;

impl LintPass for UnsafeNameRemoval {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNSAFE_REMOVED_FROM_NAME)
    }
}

impl LateLintPass for UnsafeNameRemoval {
    fn check_mod(&mut self, cx: &LateContext, m: &Mod, _: Span, _: NodeId) {
        // only check top level `use` statements
        for item in &m.item_ids {
            self.lint_item(cx, cx.krate.item(item.id));
        }
    }
}

impl UnsafeNameRemoval {
    fn lint_item(&self, cx: &LateContext, item: &Item) {
        if let ItemUse(ref item_use) = item.node {
            match item_use.node {
                ViewPath_::ViewPathSimple(ref name, ref path) => {
                    unsafe_to_safe_check(
                        path.segments
                            .last()
                            .expect("use paths cannot be empty")
                            .identifier.name,
                        *name,
                        cx, &item.span
                        );
                },
                ViewPath_::ViewPathList(_, ref path_list_items) => {
                    for path_list_item in path_list_items.iter() {
                        let plid = path_list_item.node;
                        if let (Some(name), Some(rename)) = (plid.name(), plid.rename()) {
                            unsafe_to_safe_check(name, rename, cx, &item.span);
                        };
                    }
                },
                ViewPath_::ViewPathGlob(_) => {}
            }
        }
    }
}

fn unsafe_to_safe_check(old_name: Name, new_name: Name, cx: &LateContext, span: &Span) {
    let old_str = old_name.as_str();
    let new_str = new_name.as_str();
    if contains_unsafe(&old_str) && !contains_unsafe(&new_str) {
        span_lint(
            cx,
            UNSAFE_REMOVED_FROM_NAME,
            *span,
            &format!(
                "removed \"unsafe\" from the name of `{}` in use as `{}`",
                old_str,
                new_str
            ));
    }
}

fn contains_unsafe(name: &InternedString) -> bool {
    name.contains("Unsafe") || name.contains("unsafe")
}
<commit_msg>Removed unnecessary restriction of unsafe_removed_from_name to top-level use statements<commit_after>use rustc::hir::*;
use rustc::lint::*;
use syntax::ast::Name;
use syntax::codemap::Span;
use syntax::parse::token::InternedString;
use utils::span_lint;

/// **What it does:** This lint checks for imports that remove "unsafe" from an item's name
///
/// **Why is this bad?** Renaming makes it less clear which traits and structures are unsafe.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// use std::cell::{UnsafeCell as TotallySafeCell};
///
/// extern crate crossbeam;
/// use crossbeam::{spawn_unsafe as spawn};
/// ```
declare_lint! {
    pub UNSAFE_REMOVED_FROM_NAME,
    Warn,
    "unsafe removed from name"
}

pub struct UnsafeNameRemoval;

impl LintPass for UnsafeNameRemoval {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNSAFE_REMOVED_FROM_NAME)
    }
}

impl LateLintPass for UnsafeNameRemoval {
    fn check_item(&mut self, cx: &LateContext, item: &Item) {
        if let ItemUse(ref item_use) = item.node {
            match item_use.node {
                ViewPath_::ViewPathSimple(ref name, ref path) => {
                    unsafe_to_safe_check(
                        path.segments
                            .last()
                            .expect("use paths cannot be empty")
                            .identifier.name,
                        *name,
                        cx, &item.span
                        );
                },
                ViewPath_::ViewPathList(_, ref path_list_items) => {
                    for path_list_item in path_list_items.iter() {
                        let plid = path_list_item.node;
                        if let (Some(name), Some(rename)) = (plid.name(), plid.rename()) {
                            unsafe_to_safe_check(name, rename, cx, &item.span);
                        };
                    }
                },
                ViewPath_::ViewPathGlob(_) => {}
            }
        }
    }
}

fn unsafe_to_safe_check(old_name: Name, new_name: Name, cx: &LateContext, span: &Span) {
    let old_str = old_name.as_str();
    let new_str = new_name.as_str();
    if contains_unsafe(&old_str) && !contains_unsafe(&new_str) {
        span_lint(
            cx,
            UNSAFE_REMOVED_FROM_NAME,
            *span,
            &format!(
                "removed \"unsafe\" from the name of `{}` in use as `{}`",
                old_str,
                new_str
            ));
    }
}

fn contains_unsafe(name: &InternedString) -> bool {
    name.contains("Unsafe") || name.contains("unsafe")
}
<|endoftext|>