<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! LLVM diagnostic reports.

pub use self::OptimizationDiagnosticKind::*;
pub use self::Diagnostic::*;

use libc::{c_char, c_uint};
use std::ptr;

use {ValueRef, TwineRef, DebugLocRef, DiagnosticInfoRef};

#[derive(Copy, Clone)]
pub enum OptimizationDiagnosticKind {
    OptimizationRemark,
    OptimizationMissed,
    OptimizationAnalysis,
    OptimizationFailure,
}

impl OptimizationDiagnosticKind {
    pub fn describe(self) -> &'static str {
        match self {
            OptimizationRemark => "remark",
            OptimizationMissed => "missed",
            OptimizationAnalysis => "analysis",
            OptimizationFailure => "failure",
        }
    }
}

#[derive(Copy, Clone)]
pub struct OptimizationDiagnostic {
    pub kind: OptimizationDiagnosticKind,
    pub pass_name: *const c_char,
    pub function: ValueRef,
    pub debug_loc: DebugLocRef,
    pub message: TwineRef,
}

impl OptimizationDiagnostic {
    unsafe fn unpack(kind: OptimizationDiagnosticKind, di: DiagnosticInfoRef)
            -> OptimizationDiagnostic {

        let mut opt = OptimizationDiagnostic {
            kind: kind,
            pass_name: ptr::null(),
            function: ptr::null_mut(),
            debug_loc: ptr::null_mut(),
            message: ptr::null_mut(),
        };

        super::LLVMUnpackOptimizationDiagnostic(di,
            &mut opt.pass_name,
            &mut opt.function,
            &mut opt.debug_loc,
            &mut opt.message);

        opt
    }
}

#[derive(Copy, Clone)]
pub struct InlineAsmDiagnostic {
    pub cookie: c_uint,
    pub message: TwineRef,
    pub instruction: ValueRef,
}

impl InlineAsmDiagnostic {
    unsafe fn unpack(di: DiagnosticInfoRef)
            -> InlineAsmDiagnostic {

        let mut opt = InlineAsmDiagnostic {
            cookie: 0,
            message: ptr::null_mut(),
            instruction: ptr::null_mut(),
        };

        super::LLVMUnpackInlineAsmDiagnostic(di,
            &mut opt.cookie,
            &mut opt.message,
            &mut opt.instruction);

        opt
    }
}

#[derive(Copy, Clone)]
pub enum Diagnostic {
    Optimization(OptimizationDiagnostic),
    InlineAsm(InlineAsmDiagnostic),

    /// LLVM has other types that we do not wrap here.
    UnknownDiagnostic(DiagnosticInfoRef),
}

impl Diagnostic {
    pub unsafe fn unpack(di: DiagnosticInfoRef) -> Diagnostic {
        let kind = super::LLVMGetDiagInfoKind(di);

        match kind {
            super::DK_InlineAsm
                => InlineAsm(InlineAsmDiagnostic::unpack(di)),

            super::DK_OptimizationRemark
                => Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)),

            super::DK_OptimizationRemarkMissed
                => Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di)),

            super::DK_OptimizationRemarkAnalysis
                => Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di)),

            super::DK_OptimizationFailure
                => Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di)),

            _ => UnknownDiagnostic(di)
        }
    }
}
<commit_msg>Remove unneeded `#[derive(Copy)]`<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! LLVM diagnostic reports.

pub use self::OptimizationDiagnosticKind::*;
pub use self::Diagnostic::*;

use libc::{c_char, c_uint};
use std::ptr;

use {ValueRef, TwineRef, DebugLocRef, DiagnosticInfoRef};

#[derive(Copy, Clone)]
pub enum OptimizationDiagnosticKind {
    OptimizationRemark,
    OptimizationMissed,
    OptimizationAnalysis,
    OptimizationFailure,
}

impl OptimizationDiagnosticKind {
    pub fn describe(self) -> &'static str {
        match self {
            OptimizationRemark => "remark",
            OptimizationMissed => "missed",
            OptimizationAnalysis => "analysis",
            OptimizationFailure => "failure",
        }
    }
}

pub struct OptimizationDiagnostic {
    pub kind: OptimizationDiagnosticKind,
    pub pass_name: *const c_char,
    pub function: ValueRef,
    pub debug_loc: DebugLocRef,
    pub message: TwineRef,
}

impl OptimizationDiagnostic {
    unsafe fn unpack(kind: OptimizationDiagnosticKind, di: DiagnosticInfoRef)
            -> OptimizationDiagnostic {

        let mut opt = OptimizationDiagnostic {
            kind: kind,
            pass_name: ptr::null(),
            function: ptr::null_mut(),
            debug_loc: ptr::null_mut(),
            message: ptr::null_mut(),
        };

        super::LLVMUnpackOptimizationDiagnostic(di,
            &mut opt.pass_name,
            &mut opt.function,
            &mut opt.debug_loc,
            &mut opt.message);

        opt
    }
}

#[derive(Copy, Clone)]
pub struct InlineAsmDiagnostic {
    pub cookie: c_uint,
    pub message: TwineRef,
    pub instruction: ValueRef,
}

impl InlineAsmDiagnostic {
    unsafe fn unpack(di: DiagnosticInfoRef)
            -> InlineAsmDiagnostic {

        let mut opt = InlineAsmDiagnostic {
            cookie: 0,
            message: ptr::null_mut(),
            instruction: ptr::null_mut(),
        };

        super::LLVMUnpackInlineAsmDiagnostic(di,
            &mut opt.cookie,
            &mut opt.message,
            &mut opt.instruction);

        opt
    }
}

pub enum Diagnostic {
    Optimization(OptimizationDiagnostic),
    InlineAsm(InlineAsmDiagnostic),

    /// LLVM has other types that we do not wrap here.
    UnknownDiagnostic(DiagnosticInfoRef),
}

impl Diagnostic {
    pub unsafe fn unpack(di: DiagnosticInfoRef) -> Diagnostic {
        let kind = super::LLVMGetDiagInfoKind(di);

        match kind {
            super::DK_InlineAsm
                => InlineAsm(InlineAsmDiagnostic::unpack(di)),

            super::DK_OptimizationRemark
                => Optimization(OptimizationDiagnostic::unpack(OptimizationRemark, di)),

            super::DK_OptimizationRemarkMissed
                => Optimization(OptimizationDiagnostic::unpack(OptimizationMissed, di)),

            super::DK_OptimizationRemarkAnalysis
                => Optimization(OptimizationDiagnostic::unpack(OptimizationAnalysis, di)),

            super::DK_OptimizationFailure
                => Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di)),

            _ => UnknownDiagnostic(di)
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax_pos::symbol::Symbol;
use back::write::create_target_machine;
use llvm;
use rustc::session::Session;
use rustc::session::config::PrintRequest;
use libc::{c_int, c_char};
use std::ffi::CString;

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

pub fn init(sess: &Session) {
    unsafe {
        // Before we touch LLVM, make sure that multithreading is enabled.
        static POISONED: AtomicBool = AtomicBool::new(false);
        static INIT: Once = Once::new();
        INIT.call_once(|| {
            if llvm::LLVMStartMultithreaded() != 1 {
                // use an extra bool to make sure that all future usage of LLVM
                // cannot proceed despite the Once not running more than once.
                POISONED.store(true, Ordering::SeqCst);
            }

            configure_llvm(sess);
        });

        if POISONED.load(Ordering::SeqCst) {
            bug!("couldn't enable multi-threaded LLVM");
        }
    }
}

unsafe fn configure_llvm(sess: &Session) {
    let mut llvm_c_strs = Vec::new();
    let mut llvm_args = Vec::new();

    {
        let mut add = |arg: &str| {
            let s = CString::new(arg).unwrap();
            llvm_args.push(s.as_ptr());
            llvm_c_strs.push(s);
        };
        add("rustc"); // fake program name
        if sess.time_llvm_passes() { add("-time-passes"); }
        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }

        for arg in &sess.opts.cg.llvm_args {
            add(&(*arg));
        }
    }

    llvm::LLVMInitializePasses();

    llvm::initialize_available_targets();

    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
                                 llvm_args.as_ptr());
}

// WARNING: the features must be known to LLVM or the feature
// detection code will walk past the end of the feature array,
// leading to crashes.

const ARM_WHITELIST: &'static [&'static str] = &["neon\0", "vfp2\0", "vfp3\0", "vfp4\0"];

const X86_WHITELIST: &'static [&'static str] = &["avx\0", "avx2\0", "bmi\0", "bmi2\0", "sse\0",
                                                 "sse2\0", "sse3\0", "sse4.1\0", "sse4.2\0",
                                                 "ssse3\0", "tbm\0", "lzcnt\0", "popcnt\0",
                                                 "sse4a\0", "rdrnd\0", "rdseed\0", "fma\0"];

const HEXAGON_WHITELIST: &'static [&'static str] = &["hvx\0", "hvx-double\0"];

const POWERPC_WHITELIST: &'static [&'static str] = &["altivec\0", "vsx\0"];

pub fn target_features(sess: &Session) -> Vec<Symbol> {
    let target_machine = create_target_machine(sess);

    let whitelist = match &*sess.target.target.arch {
        "arm" => ARM_WHITELIST,
        "x86" | "x86_64" => X86_WHITELIST,
        "hexagon" => HEXAGON_WHITELIST,
        "powerpc" | "powerpc64" => POWERPC_WHITELIST,
        _ => &[],
    };

    let mut features = Vec::new();
    for feat in whitelist {
        assert_eq!(feat.chars().last(), Some('\0'));
        if unsafe { llvm::LLVMRustHasFeature(target_machine, feat.as_ptr() as *const c_char) } {
            features.push(Symbol::intern(&feat[..feat.len() - 1]));
        }
    }
    features
}

pub fn print_version() {
    unsafe {
        println!("LLVM version: {}.{}",
                 llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
    }
}

pub fn print_passes() {
    unsafe { llvm::LLVMRustPrintPasses(); }
}

pub fn print(req: PrintRequest, sess: &Session) {
    let tm = create_target_machine(sess);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_trans can't handle print request: {:?}", req),
        }
    }
}

pub fn enable_llvm_debug() {
    unsafe { llvm::LLVMRustSetDebug(1); }
}
<commit_msg>Add support for the Power8 and Power9 features on PowerPC<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use syntax_pos::symbol::Symbol;
use back::write::create_target_machine;
use llvm;
use rustc::session::Session;
use rustc::session::config::PrintRequest;
use libc::{c_int, c_char};
use std::ffi::CString;

use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;

pub fn init(sess: &Session) {
    unsafe {
        // Before we touch LLVM, make sure that multithreading is enabled.
        static POISONED: AtomicBool = AtomicBool::new(false);
        static INIT: Once = Once::new();
        INIT.call_once(|| {
            if llvm::LLVMStartMultithreaded() != 1 {
                // use an extra bool to make sure that all future usage of LLVM
                // cannot proceed despite the Once not running more than once.
                POISONED.store(true, Ordering::SeqCst);
            }

            configure_llvm(sess);
        });

        if POISONED.load(Ordering::SeqCst) {
            bug!("couldn't enable multi-threaded LLVM");
        }
    }
}

unsafe fn configure_llvm(sess: &Session) {
    let mut llvm_c_strs = Vec::new();
    let mut llvm_args = Vec::new();

    {
        let mut add = |arg: &str| {
            let s = CString::new(arg).unwrap();
            llvm_args.push(s.as_ptr());
            llvm_c_strs.push(s);
        };
        add("rustc"); // fake program name
        if sess.time_llvm_passes() { add("-time-passes"); }
        if sess.print_llvm_passes() { add("-debug-pass=Structure"); }

        for arg in &sess.opts.cg.llvm_args {
            add(&(*arg));
        }
    }

    llvm::LLVMInitializePasses();

    llvm::initialize_available_targets();

    llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int,
                                 llvm_args.as_ptr());
}

// WARNING: the features must be known to LLVM or the feature
// detection code will walk past the end of the feature array,
// leading to crashes.

const ARM_WHITELIST: &'static [&'static str] = &["neon\0", "vfp2\0", "vfp3\0", "vfp4\0"];

const X86_WHITELIST: &'static [&'static str] = &["avx\0", "avx2\0", "bmi\0", "bmi2\0", "sse\0",
                                                 "sse2\0", "sse3\0", "sse4.1\0", "sse4.2\0",
                                                 "ssse3\0", "tbm\0", "lzcnt\0", "popcnt\0",
                                                 "sse4a\0", "rdrnd\0", "rdseed\0", "fma\0"];

const HEXAGON_WHITELIST: &'static [&'static str] = &["hvx\0", "hvx-double\0"];

const POWERPC_WHITELIST: &'static [&'static str] = &["altivec\0",
                                                     "power8-altivec\0", "power9-altivec\0",
                                                     "power8-vector\0", "power9-vector\0",
                                                     "vsx\0"];

pub fn target_features(sess: &Session) -> Vec<Symbol> {
    let target_machine = create_target_machine(sess);

    let whitelist = match &*sess.target.target.arch {
        "arm" => ARM_WHITELIST,
        "x86" | "x86_64" => X86_WHITELIST,
        "hexagon" => HEXAGON_WHITELIST,
        "powerpc" | "powerpc64" => POWERPC_WHITELIST,
        _ => &[],
    };

    let mut features = Vec::new();
    for feat in whitelist {
        assert_eq!(feat.chars().last(), Some('\0'));
        if unsafe { llvm::LLVMRustHasFeature(target_machine, feat.as_ptr() as *const c_char) } {
            features.push(Symbol::intern(&feat[..feat.len() - 1]));
        }
    }
    features
}

pub fn print_version() {
    unsafe {
        println!("LLVM version: {}.{}",
                 llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor());
    }
}

pub fn print_passes() {
    unsafe { llvm::LLVMRustPrintPasses(); }
}

pub fn print(req: PrintRequest, sess: &Session) {
    let tm = create_target_machine(sess);
    unsafe {
        match req {
            PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
            PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm),
            _ => bug!("rustc_trans can't handle print request: {:?}", req),
        }
    }
}

pub fn enable_llvm_debug() {
    unsafe { llvm::LLVMRustSetDebug(1); }
}
<|endoftext|>


<commit_before>/// Breadth First Search
pub mod bfs {

    use hex2d::Coordinate;

    use std::hash;
    use std::num::{SignedInt, FromPrimitive};
    use num::integer::{Integer};
    use std::collections::VecDeque;
    use std::collections::HashMap;
    use std::collections::hash_map::Entry::{Occupied,Vacant};

    struct Visited<I = i32>
        where I : SignedInt+hash::Hash {
        prev : Coordinate<I>,
        dist : u32,
    }

    /// Breadth First Search
    ///
    /// Use BFS to find closest (in walk steps) Coordinates that satisfy `is_dest` and can be
    /// reached with a walk through coordinates for which `can_pass` returns true.
    pub struct Traverser<FCanPass, FIsDest, I = i32> where
        I : SignedInt+hash::Hash,
        FCanPass : Fn(Coordinate<I>) -> bool,
        FIsDest : Fn(Coordinate<I>) -> bool
    {
        visited : HashMap<Coordinate<I>, Visited<I>>,
        to_traverse : VecDeque<Coordinate<I>>,
        can_pass : FCanPass,
        is_dest : FIsDest,
        start : Coordinate<I>,
    }

    impl<FCanPass, FIsDest, I> Traverser<FCanPass, FIsDest, I> where
        I : SignedInt+FromPrimitive+Integer+hash::Hash,
        FCanPass : Fn(Coordinate<I>) -> bool,
        FIsDest : Fn(Coordinate<I>) -> bool
    {

        /// Create a Traverser instance with initial conditions
        pub fn new(can_pass : FCanPass, is_dest : FIsDest, start: Coordinate<I>) -> Traverser<FCanPass, FIsDest, I> {
            let mut to_traverse = VecDeque::new();
            to_traverse.push_back(start);

            let mut visited = HashMap::new();
            visited.insert(start, Visited{prev: start, dist: 0});

            Traverser {
                visited: visited,
                to_traverse: to_traverse,
                can_pass: can_pass,
                is_dest: is_dest,
                start: start,
            }
        }

        /// Find next closest coordinate.
        ///
        /// Can be called multiple times, each time returning next coordinate
        pub fn find(&mut self) -> Option<Coordinate<I>> {

            loop {
                let pos = match self.to_traverse.pop_front() {
                    None => return None,
                    Some(coord) => coord,
                };

                // Traverse before returning, so `find` can be call subsequently
                // for more than just first answer
                if (self.can_pass)(pos) {

                    let &Visited{dist, ..} = self.visited.get(&pos).expect("BFS: Should have been visited already");

                    let dist = dist + 1;

                    for &npos in pos.neighbors().iter() {
                        match self.visited.entry(npos) {
                            Occupied(_) => { /* already visited */ }
                            Vacant(entry) => {
                                entry.insert(Visited{prev: pos, dist: dist});
                                self.to_traverse.push_back(npos);
                            }
                        }
                    }
                }

                if (self.is_dest)(pos) {
                    return Some(pos);
                }
            }
        }

        /// Return neighbor Coordinate to `pos` that is one step closer to
        /// `start` from initial conditions.
        ///
        /// Useful for finding whole path to a Coordinate returned by `find`.
        ///
        /// Returns `None` for Coordinates that were not yet visited.
        /// Returns `start` for `start` (from initial conditions)
        pub fn backtrace(&self, pos : Coordinate<I>) -> Option<Coordinate<I>> {
            self.visited.get(&pos).map(|entry| entry.prev)
        }

        /// Perform a recursive `backtrace` walk to find a neighbor of `start` that leads to the
        /// Coordinate returned by `find()`.
        ///
        /// Returns `None` for Coordinates that were not yet visited.
        /// Returns `start` for `start` (from initial conditions)
        pub fn backtrace_last(&self, mut pos : Coordinate<I>) -> Option<Coordinate<I>> {
            loop {
                pos = match self.visited.get(&pos) {
                    None => return None,
                    Some(entry) => {
                        if entry.prev == self.start {
                            return Some(pos);
                        } else {
                            entry.prev
                        }
                    }
                }
            }
        }
    }
}

/// Very tricky, but (hopefully) good enough, recursive LoS algorithm
pub mod los {
    use hex2d::Angle::{Left,Right};
    use hex2d::Direction;
    use hex2d::Coordinate;
    use std::num::{SignedInt, FromPrimitive};
    use num::integer::{Integer};

    fn los_rec<FOpaqueness, FVisible, I=i32>(
        opaqueness : &FOpaqueness,
        visible : &mut FVisible,
        light: I,
        pos : Coordinate<I>,
        start_dir : Direction,
        main_dir : Direction,
        dir : Option<Direction>,
        pdir : Option<Direction>,
    ) where
        I : SignedInt+FromPrimitive+Integer,
        FOpaqueness : Fn(Coordinate<I>) -> I,
        FVisible : FnMut(Coordinate<I>, I)
        {
            visible(pos, light);

            let mut light = light;
            let opaq = opaqueness(pos);

            if opaq >= light {
                return;
            } else {
                light = light - opaq;
            }

            // Handle long corridors a bit better
            if main_dir + Left == start_dir || main_dir == start_dir {
                visible(pos + (main_dir + Left), light);
            }
            if main_dir + Right == start_dir || main_dir == start_dir {
                visible(pos + (main_dir + Right), light);
            }

            let neighbors = match (dir, pdir) {
                (Some(dir), Some(pdir)) => {
                    if dir == pdir {
                        vec!(dir)
                    } else {
                        vec!(dir, pdir)
                    }
                },
                (Some(dir), None) => {
                    if main_dir == dir {
                        vec!(dir, dir + Left, dir + Right)
                    } else {
                        vec!(dir, main_dir)
                    }
                },
                _ => {
                    vec!(main_dir, main_dir + Left, main_dir + Right)
                }
            };

            for &d in neighbors.iter() {
                let npos = pos + d;
                match dir {
                    Some(_) => los_rec::<FOpaqueness, FVisible, I>(opaqueness, visible, light, npos, start_dir, d, Some(d), dir),
                    None => los_rec::<FOpaqueness, FVisible, I>(opaqueness, visible, light, npos, start_dir, main_dir, Some(d), dir),
                }
            }
        }

    /// Starting from `pos` in direction `dir`, call `visible` for each visible Coordinate.
    ///
    /// Use `light` as starting range of the LoS. For each visible Coordinate, the value returned
    /// by `opaqueness` will be subtracted from `light` to check if the LoS should finish due to
    /// "lack of visibility". `opaqueness` should typically return 1 for fully transparent
    /// Coordinates, and anything bigger than initial `light` for fully opaque Coordinates.
    pub fn los<FOpaqueness, FVisible, I=i32>(
        opaqueness : &FOpaqueness,
        visible : &mut FVisible,
        light: I,
        pos : Coordinate<I>,
        dirs : &[Direction],
    ) where
        I : SignedInt+FromPrimitive+Integer,
        FOpaqueness : Fn(Coordinate<I>) -> I,
        FVisible : FnMut(Coordinate<I>, I)
        {
            for dir in dirs.iter() {
                los_rec::<FOpaqueness, FVisible, I>(opaqueness, visible, light, pos, *dir, *dir, None, None);
            }
        }
}
<commit_msg>Implement new LoS.<commit_after>/// Breadth First Search
pub mod bfs {

    use hex2d::Coordinate;

    use std::hash;
    use std::num::{SignedInt, FromPrimitive};
    use num::integer::{Integer};
    use std::collections::VecDeque;
    use std::collections::HashMap;
    use std::collections::hash_map::Entry::{Occupied,Vacant};

    struct Visited<I = i32>
        where I : SignedInt+hash::Hash {
        prev : Coordinate<I>,
        dist : u32,
    }

    /// Breadth First Search
    ///
    /// Use BFS to find closest (in walk steps) Coordinates that satisfy `is_dest` and can be
    /// reached with a walk through coordinates for which `can_pass` returns true.
    pub struct Traverser<FCanPass, FIsDest, I = i32> where
        I : SignedInt+hash::Hash,
        FCanPass : Fn(Coordinate<I>) -> bool,
        FIsDest : Fn(Coordinate<I>) -> bool
    {
        visited : HashMap<Coordinate<I>, Visited<I>>,
        to_traverse : VecDeque<Coordinate<I>>,
        can_pass : FCanPass,
        is_dest : FIsDest,
        start : Coordinate<I>,
    }

    impl<FCanPass, FIsDest, I> Traverser<FCanPass, FIsDest, I> where
        I : SignedInt+FromPrimitive+Integer+hash::Hash,
        FCanPass : Fn(Coordinate<I>) -> bool,
        FIsDest : Fn(Coordinate<I>) -> bool
    {

        /// Create a Traverser instance with initial conditions
        pub fn new(can_pass : FCanPass, is_dest : FIsDest, start: Coordinate<I>) -> Traverser<FCanPass, FIsDest, I> {
            let mut to_traverse = VecDeque::new();
            to_traverse.push_back(start);

            let mut visited = HashMap::new();
            visited.insert(start, Visited{prev: start, dist: 0});

            Traverser {
                visited: visited,
                to_traverse: to_traverse,
                can_pass: can_pass,
                is_dest: is_dest,
                start: start,
            }
        }

        /// Find next closest coordinate.
        ///
        /// Can be called multiple times, each time returning next coordinate
        pub fn find(&mut self) -> Option<Coordinate<I>> {

            loop {
                let pos = match self.to_traverse.pop_front() {
                    None => return None,
                    Some(coord) => coord,
                };

                // Traverse before returning, so `find` can be call subsequently
                // for more than just first answer
                if (self.can_pass)(pos) {

                    let &Visited{dist, ..} = self.visited.get(&pos).expect("BFS: Should have been visited already");

                    let dist = dist + 1;

                    for &npos in pos.neighbors().iter() {
                        match self.visited.entry(npos) {
                            Occupied(_) => { /* already visited */ }
                            Vacant(entry) => {
                                entry.insert(Visited{prev: pos, dist: dist});
                                self.to_traverse.push_back(npos);
                            }
                        }
                    }
                }

                if (self.is_dest)(pos) {
                    return Some(pos);
                }
            }
        }

        /// Return neighbor Coordinate to `pos` that is one step closer to
        /// `start` from initial conditions.
        ///
        /// Useful for finding whole path to a Coordinate returned by `find`.
        ///
        /// Returns `None` for Coordinates that were not yet visited.
        /// Returns `start` for `start` (from initial conditions)
        pub fn backtrace(&self, pos : Coordinate<I>) -> Option<Coordinate<I>> {
            self.visited.get(&pos).map(|entry| entry.prev)
        }

        /// Perform a recursive `backtrace` walk to find a neighbor of `start` that leads to the
        /// Coordinate returned by `find()`.
        ///
        /// Returns `None` for Coordinates that were not yet visited.
        /// Returns `start` for `start` (from initial conditions)
        pub fn backtrace_last(&self, mut pos : Coordinate<I>) -> Option<Coordinate<I>> {
            loop {
                pos = match self.visited.get(&pos) {
                    None => return None,
                    Some(entry) => {
                        if entry.prev == self.start {
                            return Some(pos);
                        } else {
                            entry.prev
                        }
                    }
                }
            }
        }
    }
}

/// Very tricky, but (hopefully) good enough, recursive LoS algorithm
pub mod los {
    use hex2d::Angle::{Left,Right};
    use hex2d::Direction;
    use hex2d::Coordinate;
    use std::num::{SignedInt, FromPrimitive};
    use num::integer::{Integer};

    fn los_rec<FOpaqueness, FVisible, I=i32>(
        opaqueness : &FOpaqueness,
        visible : &mut FVisible,
        light: I,
        pos : Coordinate<I>,
        start_dir : Direction,
        main_dir : Direction,
        dir : Option<Direction>,
        pdir : Option<Direction>,
    ) where
        I : SignedInt+FromPrimitive+Integer,
        FOpaqueness : Fn(Coordinate<I>) -> I,
        FVisible : FnMut(Coordinate<I>, I)
        {
            visible(pos, light);

            let mut light = light;
            let opaq = opaqueness(pos);

            if opaq >= light {
                return;
            } else {
                light = light - opaq;
            }

            // Handle long corridors a bit better
            if main_dir + Left == start_dir || main_dir == start_dir {
                visible(pos + (main_dir + Left), light);
            }
            if main_dir + Right == start_dir || main_dir == start_dir {
                visible(pos + (main_dir + Right), light);
            }

            let neighbors = match (dir, pdir) {
                (Some(dir), Some(pdir)) => {
                    if dir == pdir {
                        vec!(dir)
                    } else {
                        vec!(dir, pdir)
                    }
                },
                (Some(dir), None) => {
                    if main_dir == dir {
                        vec!(dir, dir + Left, dir + Right)
                    } else {
                        vec!(dir, main_dir)
                    }
                },
                _ => {
                    vec!(main_dir, main_dir + Left, main_dir + Right)
                }
            };

            for &d in neighbors.iter() {
                let npos = pos + d;
                match dir {
                    Some(_) => los_rec::<FOpaqueness, FVisible, I>(opaqueness, visible, light, npos, start_dir, d, Some(d), dir),
                    None => los_rec::<FOpaqueness, FVisible, I>(opaqueness, visible, light, npos, start_dir, main_dir, Some(d), dir),
                }
            }
        }

    /// Starting from `pos` in direction `dir`, call `visible` for each visible Coordinate.
    ///
    /// Use `light` as starting range of the LoS. For each visible Coordinate, the value returned
    /// by `opaqueness` will be subtracted from `light` to check if the LoS should finish due to
    /// "lack of visibility". `opaqueness` should typically return 1 for fully transparent
    /// Coordinates, and anything bigger than initial `light` for fully opaque Coordinates.
    pub fn los<FOpaqueness, FVisible, I=i32>(
        opaqueness : &FOpaqueness,
        visible : &mut FVisible,
        light: I,
        pos : Coordinate<I>,
        dirs : &[Direction],
    ) where
        I : SignedInt+FromPrimitive+Integer,
        FOpaqueness : Fn(Coordinate<I>) -> I,
        FVisible : FnMut(Coordinate<I>, I)
        {
            for dir in dirs.iter() {
                los_rec::<FOpaqueness, FVisible, I>(opaqueness, visible, light, pos, *dir, *dir, None, None);
            }
        }
}

/// Combination of tricky Los with straight line checking
pub mod los2 {
    use hex2d::Angle::{Left, Right, Forward};
    use hex2d::Direction;
    use hex2d::Coordinate;
    use std::num::{SignedInt, FromPrimitive};
    use num::integer::{Integer};
    use std::collections::HashSet;
    use std::hash::Hash;

    fn los_rec<FOpaqueness, FVisible, I=i32>(
        opaqueness : &FOpaqueness,
        visible : &mut FVisible,
        light: I,
        start : Coordinate<I>,
        pos : Coordinate<I>,
        dir : Direction,
        visited : &mut HashSet<Coordinate<I>>,
    ) where
        I : SignedInt+FromPrimitive+Integer+Hash+Eq,
        FOpaqueness : Fn(Coordinate<I>) -> I,
        FVisible : FnMut(Coordinate<I>, I)
        {
            if visited.contains(&pos) {
                return;
            } else {
                visited.insert(pos);
            }

            let mut opaq_sum = FromPrimitive::from_i8(0).unwrap();
            let mut last = pos;
            let pos_opaq = opaqueness(pos);
            let opaq_limit_still_visible = FromPrimitive::from_i8(5).unwrap();

            start.for_each_in_line_to(pos, |c| {
                let opaq = opaqueness(c);
                opaq_sum = opaq_sum + opaq;

                if opaq_sum >= light {
                    last = c;
                    return;
                }
            });

            if light > opaq_sum {
                visible(pos, light - opaq_sum);
            } else {
                if last == pos || pos_opaq > opaq_limit_still_visible {
                    visible(pos, light - light);
                }
                return;
            }

            let neighbors = vec!(Forward, Left, Right);

            for &a in neighbors.iter() {
                let npos = pos + (dir + a);
                los_rec::<FOpaqueness, FVisible, I>(
                    opaqueness, visible, light, start, npos, dir, visited
                    );
            }
        }

    /// Starting from `pos` in direction `dir`, call `visible` for each visible Coordinate.
    ///
    /// Use `light` as starting range of the LoS. For each visible Coordinate, the value returned
    /// by `opaqueness` will be subtracted from `light` to check if the LoS should finish due to
    /// "lack of visibility". `opaqueness` should typically return 1 for fully transparent
    /// Coordinates, and anything bigger than initial `light` for fully opaque Coordinates.
    pub fn los<FOpaqueness, FVisible, I=i32>(
        opaqueness : &FOpaqueness,
        visible : &mut FVisible,
        light: I,
        pos : Coordinate<I>,
        dirs : &[Direction],
    ) where
        I : SignedInt+FromPrimitive+Integer+Hash+Eq,
        FOpaqueness : Fn(Coordinate<I>) -> I,
        FVisible : FnMut(Coordinate<I>, I)
        {
            for dir in dirs.iter() {
                let mut visited = HashSet::new();
                los_rec::<FOpaqueness, FVisible, I>(
                    opaqueness, visible, light, pos, pos, *dir, &mut visited
                    );
            }
        }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
    let _x = "test" as &::std::any::Any;
//~^ ERROR the trait `core::kinds::Sized` is not implemented for the type `str`
//~^^ NOTE the trait `core::kinds::Sized` must be implemented for the cast to the object type
//~^^^ ERROR the trait `core::kinds::Sized` is not implemented for the type `str`
//~^^^^ NOTE the trait `core::kinds::Sized` must be implemented for the cast to the object type
}
<commit_msg>Revise DST test to not require duplicate errors.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
    let _x = "test" as &::std::any::Any;
//~^ ERROR the trait `core::kinds::Sized` is not implemented for the type `str`
//~^^ NOTE the trait `core::kinds::Sized` must be implemented for the cast to the object type
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Makes sure that zero-initializing large types is reasonably fast,
// Doing it incorrectly causes massive slowdown in LLVM during
// optimisation.

#![feature(intrinsics)]

extern "rust-intrinsic" {
    pub fn init<T>() -> T;
}

const SIZE: usize = 1024 * 1024;

fn main() {
    let _memory: [u8; SIZE] = unsafe { init() };
}
<commit_msg>Change init-large-type to use child thread<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Makes sure that zero-initializing large types is reasonably fast,
// Doing it incorrectly causes massive slowdown in LLVM during
// optimisation.

#![feature(intrinsics)]

use std::thread::Thread;

extern "rust-intrinsic" {
    pub fn init<T>() -> T;
}

const SIZE: usize = 1024 * 1024;

fn main() {
    // do the test in a new thread to avoid (spurious?) stack overflows
    let _ = Thread::scoped(|| {
        let _memory: [u8; SIZE] = unsafe { init() };
    }).join();
}
<|endoftext|>


<commit_before>// run-pass

#![allow(unused_must_use)]
#![feature(unwind_attributes)]
// Since we mark some ABIs as "nounwind" to LLVM, we must make sure that
// we never unwind through them.

// ignore-emscripten no processes
// ignore-sgx no processes

use std::{env, panic};
use std::io::prelude::*;
use std::io;
use std::process::{Command, Stdio};

#[unwind(aborts)] // FIXME(#58794) should work even without the attribute
extern "C" fn panic_in_ffi() {
    panic!("Test");
}

#[unwind(aborts)]
extern "Rust" fn panic_in_rust_abi() {
    panic!("TestRust");
}

fn should_have_aborted() {
    io::stdout().write(b"This should never be printed.\n");
    let _ = io::stdout().flush();
}

fn bomb_out_but_not_abort(msg: &str) {
    eprintln!("bombing out: {}", msg);
    exit(1);
}

fn test() {
    let _ = panic::catch_unwind(|| { panic_in_ffi(); });
    should_have_aborted();
}

fn testrust() {
    let _ = panic::catch_unwind(|| { panic_in_rust_abi(); });
    should_have_aborted();
}

fn test_always_abort() {
    panic::always_abort();
    let _ = panic::catch_unwind(|| { panic!(); });
    should_have_aborted();
}

fn main() {
    let tests: &[(_, fn())] = &[
        ("test", test),
        ("testrust", testrust),
        ("test_always_abort", test_always_abort),
    ];

    let args: Vec<String> = env::args().collect();
    if args.len() > 1 {
        // This is inside the self-executed command.
        for (a,f) in tests {
            if &args[1] == a { return f() }
        }
        bomb_out_but_not_abort("bad test");
    }

    let execute_self_expecting_abort = |arg| {
        let mut p = Command::new(&args[0])
                            .stdout(Stdio::piped())
                            .stdin(Stdio::piped())
                            .arg(arg).spawn().unwrap();
        let status = p.wait().unwrap();
        assert!(!status.success());
        // Any reasonable platform can distinguish a process which
        // called exit(1) from one which panicked.
        assert_ne!(status.code(), Some(1));
    };

    for (a,_f) in tests {
        execute_self_expecting_abort(a);
    }
}
<commit_msg>panic ui test: Test always_abort on one thread, panic on another<commit_after>// run-pass

#![allow(unused_must_use)]
#![feature(unwind_attributes)]
#![feature(panic_always_abort)]
// Since we mark some ABIs as "nounwind" to LLVM, we must make sure that
// we never unwind through them.

// ignore-emscripten no processes
// ignore-sgx no processes

use std::{env, panic};
use std::io::prelude::*;
use std::io;
use std::process::{exit, Command, Stdio};
use std::sync::{Arc, Barrier};
use std::thread;

#[unwind(aborts)] // FIXME(#58794) should work even without the attribute
extern "C" fn panic_in_ffi() {
    panic!("Test");
}

#[unwind(aborts)]
extern "Rust" fn panic_in_rust_abi() {
    panic!("TestRust");
}

fn should_have_aborted() {
    io::stdout().write(b"This should never be printed.\n");
    let _ = io::stdout().flush();
}

fn bomb_out_but_not_abort(msg: &str) {
    eprintln!("bombing out: {}", msg);
    exit(1);
}

fn test() {
    let _ = panic::catch_unwind(|| { panic_in_ffi(); });
    should_have_aborted();
}

fn testrust() {
    let _ = panic::catch_unwind(|| { panic_in_rust_abi(); });
    should_have_aborted();
}

fn test_always_abort() {
    panic::always_abort();
    let _ = panic::catch_unwind(|| { panic!(); });
    should_have_aborted();
}

fn test_always_abort_thread() {
    let barrier = Arc::new(Barrier::new(2));
    let thr = {
        let barrier = barrier.clone();
        thread::spawn(move ||{
            barrier.wait();
            panic!("in thread");
        })
    };
    panic::always_abort();
    barrier.wait();
    let _ = thr.join();
    bomb_out_but_not_abort("joined - but we were supposed to panic!");
}

fn main() {
    let tests: &[(_, fn())] = &[
        ("test", test),
        ("testrust", testrust),
        ("test_always_abort", test_always_abort),
        ("test_always_abort_thread", test_always_abort_thread),
    ];

    let args: Vec<String> = env::args().collect();
    if args.len() > 1 {
        // This is inside the self-executed command.
        for (a,f) in tests {
            if &args[1] == a { return f() }
        }
        bomb_out_but_not_abort("bad test");
    }

    let execute_self_expecting_abort = |arg| {
        let mut p = Command::new(&args[0])
                            .stdout(Stdio::piped())
                            .stdin(Stdio::piped())
                            .arg(arg).spawn().unwrap();
        let status = p.wait().unwrap();
        assert!(!status.success());
        // Any reasonable platform can distinguish a process which
        // called exit(1) from one which panicked.
        assert_ne!(status.code(), Some(1));
    };

    for (a,_f) in tests {
        execute_self_expecting_abort(a);
    }
}
<|endoftext|>


<commit_before>use std::collections::{BTreeSet, HashMap};
use std::collections::hash_map::Entry as HashMapEntry;
use std::path::PathBuf;
use std::result::Result::Ok;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Duration;
use std::vec::Vec;

use data::{Entry, Hint, SequenceNumber};
use errors::Result;
use log::{Log, LogWrite};
use stats::Stats;

const COMPACTION_CHECK_FREQUENCY: u64 = 60;
const FRAGMENTATION_TRIGGER: f64 = 0.6;
const DEAD_BYTES_TRIGGER: u64 = 512 * 1024 * 1024;
const FRAGMENTATION_THRESHOLD: f64 = 0.4;
const DEAD_BYTES_THRESHOLD: u64 = 128 * 1024 * 1024;
const SMALL_FILE_THRESHOLD: u64 = 10 * 1024 * 1024;

#[derive(Debug)]
pub struct IndexEntry {
    pub file_id: u32,
    entry_pos: u64,
    pub entry_size: u64,
    sequence: SequenceNumber,
}

struct Index {
    map: HashMap<Vec<u8>, IndexEntry>,
    stats: Stats,
}

impl Index {
    fn new() -> Index {
        Index {
            map: HashMap::new(),
            stats: Stats::new(),
        }
    }

    fn get(&self, key: &[u8]) -> Option<&IndexEntry> {
        self.map.get(key)
    }

    fn insert(&mut self, key: Vec<u8>, index_entry: IndexEntry) -> Option<IndexEntry> {
        self.stats.add_entry(&index_entry);
        self.map
            .insert(key, index_entry)
            .map(|entry| {
                self.stats.remove_entry(&entry);
                entry
            })
    }

    fn remove(&mut self, key: &[u8]) -> Option<IndexEntry> {
        self.map
            .remove(key)
            .map(|entry| {
                self.stats.remove_entry(&entry);
                entry
            })
    }

    fn update(&mut self, hint: Hint, file_id: u32) {
        let index_entry = IndexEntry {
            file_id: file_id,
            entry_pos: hint.entry_pos,
            entry_size: hint.entry_size(),
            sequence: hint.sequence,
        };

        match self.map.entry(hint.key.to_vec()) {
            HashMapEntry::Occupied(mut o) => {
                if o.get().sequence <= hint.sequence {
                    self.stats.remove_entry(o.get());
                    if hint.deleted {
                        o.remove();
                    } else {
                        self.stats.add_entry(&index_entry);
                        o.insert(index_entry);
                    }
                } else {
                    self.stats.add_entry(&index_entry);
                    self.stats.remove_entry(&index_entry);
                }
            }
            HashMapEntry::Vacant(e) => {
                if !hint.deleted {
                    self.stats.add_entry(&index_entry);
                    e.insert(index_entry);
                }
            }
        }
    }
}

struct CaskInner {
    current_sequence: SequenceNumber,
    index: Index,
    log: Log,
}

impl CaskInner {
    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
        let value = match self.index.get(key) {
            Some(index_entry) => {
                let entry = self.log
                    .read_entry(index_entry.file_id, index_entry.entry_pos)?;
                if entry.deleted {
                    warn!("Index pointed to dead entry: Entry {{ key: {:?}, sequence: {} }} at \
                           file: {}",
                          entry.key,
                          entry.sequence,
                          index_entry.file_id);
                    None
                } else {
                    Some(entry.value.into_owned())
                }
            }
            _ => None,
        };

        Ok(value)
    }

    fn put(&mut self, key: Vec<u8>, value: &[u8]) -> Result<()> {
        let index_entry = {
            let entry = Entry::new(self.current_sequence, &*key, value)?;

            let (file_id, file_pos) = self.log.append_entry(&entry)?;

            self.current_sequence += 1;

            IndexEntry {
                file_id: file_id,
                entry_pos: file_pos,
                entry_size: entry.size(),
                sequence: entry.sequence,
            }
        };

        self.index.insert(key, index_entry);

        Ok(())
    }

    fn delete(&mut self, key: &[u8]) -> Result<()> {
        if self.index.remove(key).is_some() {
            let entry = Entry::deleted(self.current_sequence, key);
            self.log.append_entry(&entry)?;
            self.current_sequence += 1;
        }

        Ok(())
    }
}

#[derive(Clone)]
pub struct Cask {
    path: PathBuf,
    dropped: Arc<AtomicBool>,
    inner: Arc<RwLock<CaskInner>>,
}

impl Cask {
    pub fn open(path: &str, sync: bool) -> Result<Cask> {
        info!("Opening database: {:?}", &path);
        let mut log = Log::open(path, sync)?;
        let mut index = Index::new();

        let mut sequence = 0;

        for file_id in log.files() {
            let mut f = |hint: Hint| {
                if hint.sequence > sequence {
                    sequence = hint.sequence;
                }

                index.update(hint, file_id);
            };

            match log.hints(file_id)? {
                Some(hints) => {
                    for hint in hints {
                        f(hint?);
                    }
                }
                None => {
                    for hint in log.recreate_hints(file_id)? {
                        f(hint?);
                    }
                }
            };
        }

        info!("Opened database: {:?}", &path);
        info!("Current sequence number: {:?}", sequence);

        let cask = Cask {
            path: log.path.clone(),
            dropped: Arc::new(AtomicBool::new(false)),
            inner: Arc::new(RwLock::new(CaskInner {
                                            current_sequence: sequence + 1,
                                            log: log,
                                            index: index,
                                        })),
        };

        let caskt = cask.clone();
        thread::spawn(move || loop {
                          thread::sleep(Duration::new(COMPACTION_CHECK_FREQUENCY, 0));

                          info!("Compaction thread wake up");

                          if caskt.dropped.load(Ordering::SeqCst) {
                              info!("Cask has been dropped, background compaction \
                                     thread is exiting");
                              break;
                          }

                          if let Err(err) = caskt.compact() {
                              warn!("Error during compaction: {}", err);
                          }
                      });

        Ok(cask)
    }

    fn compact_files_aux(&self, files: &[u32]) -> Result<(Vec<u32>, Vec<u32>)> {
        let active_file_id = {
            self.inner.read().unwrap().log.active_file_id
        };

        let compacted_files_hints = files
            .iter()
            .flat_map(|&file_id| {
                if active_file_id.is_some() && active_file_id.unwrap() == file_id {
                    None
                } else {
                    self.inner
                        .read()
                        .unwrap()
                        .log
                        .hints(file_id)
                        .ok() // FIXME: log the error?
                        .and_then(|hints| hints.map(|h| (file_id, h)))
                }
            });

        let mut compacted_files = Vec::new();
        let mut new_files = Vec::new();
        let mut deletes = HashMap::new();

        let mut log_writer = {
            // FIXME: turn into error
            self.inner.read().unwrap().log.writer()
        };

        for (file_id, hints) in compacted_files_hints {
            let mut inserts = Vec::new();

            for hint in hints {
                let hint = hint?;
                let inner = self.inner.read().unwrap();
                let index_entry = inner.index.get(&*hint.key);

                if hint.deleted {
                    if index_entry.is_none() {
                        match deletes.entry(hint.key.to_vec()) {
                            HashMapEntry::Occupied(mut o) => {
                                if *o.get() < hint.sequence {
                                    o.insert(hint.sequence);
                                }
                            }
                            HashMapEntry::Vacant(e) => {
                                e.insert(hint.sequence);
                            }
                        }
                    }
                } else if index_entry.is_some() && index_entry.unwrap().sequence == hint.sequence {
                    inserts.push(hint)
                }
            }

            for hint in inserts {
                // FIXME: turn into error
                let log = &self.inner.read().unwrap().log;
                let log_write = log_writer.write(&log.read_entry(file_id, hint.entry_pos)?)?;

                if let LogWrite::NewFile(file_id) = log_write {
                    new_files.push(file_id);
                }
            }

            compacted_files.push(file_id);
        }

        for (key, sequence) in deletes {
            log_writer.write(&Entry::deleted(sequence, key))?;
        }

        Ok((compacted_files, new_files))
    }

    pub fn compact_files(&self, files: &[u32]) -> Result<()> {
        info!("Compacting data files: {:?}", files);

        let (ref compacted_files, ref new_files) = self.compact_files_aux(files)?;

        for &file_id in new_files {
            let hints = {
                self.inner.read().unwrap().log.hints(file_id)?
            };

            if let Some(hints) = hints {
                for hint in hints {
                    let hint = hint?;
                    self.inner.write().unwrap().index.update(hint, file_id);
                }
            };
        }

        self.inner
            .write()
            .unwrap()
            .index
            .stats
            .remove_files(compacted_files);

        self.inner
            .write()
            .unwrap()
            .log
            .swap_files(compacted_files, new_files)?;

        // FIXME: print files not compacted
        info!("Finished compacting data files: {:?} into: {:?}",
              compacted_files,
              new_files);

        Ok(())
    }

    // FIXME: add compaction lock
    pub fn compact(&self) -> Result<()> {
        let active_file_id = {
            self.inner.read().unwrap().log.active_file_id
        };

        let file_stats = {
            self.inner.read().unwrap().index.stats.file_stats()
        };

        let mut files = BTreeSet::new();
        let mut triggered = false;

        for (file_id, fragmentation, dead_bytes) in file_stats {
            if active_file_id.is_some() && file_id == active_file_id.unwrap() {
                continue;
            }

            if !triggered {
                if fragmentation >= FRAGMENTATION_TRIGGER {
                    info!("File {} has fragmentation factor of {}%, triggered compaction",
                          file_id,
                          fragmentation * 100.0);
                    triggered = true;
                    files.insert(file_id);
                } else if dead_bytes >= DEAD_BYTES_TRIGGER {
                    if !files.contains(&file_id) {
                        info!("File {} has {} dead bytes, triggered compaction",
                              file_id,
                              dead_bytes);
                        triggered = true;
                        files.insert(file_id);
                    }
                }
            }

            if fragmentation >= FRAGMENTATION_THRESHOLD {
                if !files.contains(&file_id) {
                    info!("File {} has fragmentation factor of {}%, adding for compaction",
                          file_id,
                          fragmentation * 100.0);
                    files.insert(file_id);
                }
            } else if dead_bytes >= DEAD_BYTES_THRESHOLD {
                if !files.contains(&file_id) {
                    info!("File {} has {} dead bytes, adding for compaction",
                          file_id,
                          dead_bytes);
                    files.insert(file_id);
                }
            }

            if !files.contains(&file_id) {
                let file_size = {
                    self.inner.read().unwrap().log.file_size(file_id).ok()
                };

                if let Some(file_size) = file_size {
                    if file_size <= SMALL_FILE_THRESHOLD {
                        info!("File {} has total size of {} bytes, adding for compaction",
                              file_id,
                              file_size);
                        files.insert(file_id);
                    }
                };
            }
        }

        if triggered {
            let files: Vec<_> = files.into_iter().collect();
            self.compact_files(&files)?;
        } else {
            if files.is_empty() {
                info!("No files eligible for compaction")
            } else {
                info!("Compaction of files {:?} aborted due to missing trigger",
                      &files);
            }
        }

        Ok(())
    }

    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Vec<u8>>> {
        self.inner.read().unwrap().get(key.as_ref())
    }

    pub fn put<K: Into<Vec<u8>>, V: AsRef<[u8]>>(&self, key: K, value: V) -> Result<()> {
        self.inner.write().unwrap().put(key.into(), value.as_ref())
    }

    pub fn delete<K: AsRef<[u8]>>(&self, key: K) -> Result<()> {
        self.inner.write().unwrap().delete(key.as_ref())
    }
}

impl Drop for Cask {
    fn drop(&mut self) {
        self.dropped.store(true, Ordering::SeqCst);
    }
}
<commit_msg>add compaction lock<commit_after>use std::collections::{BTreeSet, HashMap};
use std::collections::hash_map::Entry as HashMapEntry;
use std::path::PathBuf;
use std::result::Result::Ok;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::thread;
use std::time::Duration;
use std::vec::Vec;

use data::{Entry, Hint, SequenceNumber};
use errors::Result;
use log::{Log, LogWrite};
use stats::Stats;

const COMPACTION_CHECK_FREQUENCY: u64 = 60;
const FRAGMENTATION_TRIGGER: f64 = 0.6;
const DEAD_BYTES_TRIGGER: u64 = 512 * 1024 * 1024;
const FRAGMENTATION_THRESHOLD: f64 = 0.4;
const DEAD_BYTES_THRESHOLD: u64 = 128 * 1024 * 1024;
const SMALL_FILE_THRESHOLD: u64 = 10 * 1024 * 1024;

#[derive(Debug)]
pub struct IndexEntry {
    pub file_id: u32,
    entry_pos: u64,
    pub entry_size: u64,
    sequence: SequenceNumber,
}

struct Index {
    map: HashMap<Vec<u8>, IndexEntry>,
    stats: Stats,
}

impl Index {
    fn new() -> Index {
        Index {
            map: HashMap::new(),
            stats: Stats::new(),
        }
    }

    fn get(&self, key: &[u8]) -> Option<&IndexEntry> {
        self.map.get(key)
    }

    fn insert(&mut self, key: Vec<u8>, index_entry: IndexEntry) -> Option<IndexEntry> {
        self.stats.add_entry(&index_entry);
        self.map
            .insert(key, index_entry)
            .map(|entry| {
                self.stats.remove_entry(&entry);
                entry
            })
    }

    fn remove(&mut self, key: &[u8]) -> Option<IndexEntry> {
        self.map
            .remove(key)
            .map(|entry| {
                self.stats.remove_entry(&entry);
                entry
            })
    }

    fn update(&mut self, hint: Hint, file_id: u32) {
        let index_entry = IndexEntry {
            file_id: file_id,
            entry_pos: hint.entry_pos,
            entry_size: hint.entry_size(),
            sequence: hint.sequence,
        };

        match self.map.entry(hint.key.to_vec()) {
            HashMapEntry::Occupied(mut o) => {
                if o.get().sequence <= hint.sequence {
                    self.stats.remove_entry(o.get());
                    if hint.deleted {
                        o.remove();
                    } else {
                        self.stats.add_entry(&index_entry);
                        o.insert(index_entry);
                    }
                } else {
                    self.stats.add_entry(&index_entry);
                    self.stats.remove_entry(&index_entry);
                }
            }
            HashMapEntry::Vacant(e) => {
                if !hint.deleted {
                    self.stats.add_entry(&index_entry);
                    e.insert(index_entry);
                }
            }
        }
    }
}

struct CaskInner {
    current_sequence: SequenceNumber,
    index: Index,
    log: Log,
}

impl CaskInner {
    fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
        let value = match self.index.get(key) {
            Some(index_entry) => {
                let entry = self.log
                    .read_entry(index_entry.file_id, index_entry.entry_pos)?;
                if entry.deleted {
                    warn!("Index pointed to dead entry: Entry {{ key: {:?}, sequence: {} }} at \
                           file: {}",
                          entry.key,
                          entry.sequence,
                          index_entry.file_id);
                    None
                } else {
                    Some(entry.value.into_owned())
                }
            }
            _ => None,
        };

        Ok(value)
    }

    fn put(&mut self, key: Vec<u8>, value: &[u8]) -> Result<()> {
        let index_entry = {
            let entry = Entry::new(self.current_sequence, &*key, value)?;

            let (file_id, file_pos) = self.log.append_entry(&entry)?;

            self.current_sequence += 1;

            IndexEntry {
                file_id: file_id,
                entry_pos: file_pos,
                entry_size: entry.size(),
                sequence: entry.sequence,
            }
        };

        self.index.insert(key, index_entry);

        Ok(())
    }

    fn delete(&mut self, key: &[u8]) -> Result<()> {
        if self.index.remove(key).is_some() {
            let entry = Entry::deleted(self.current_sequence, key);
            self.log.append_entry(&entry)?;
            self.current_sequence += 1;
        }

        Ok(())
    }
}

#[derive(Clone)]
pub struct Cask {
    path: PathBuf,
    dropped: Arc<AtomicBool>,
    inner: Arc<RwLock<CaskInner>>,
    compaction: Arc<Mutex<()>>,
}

impl Cask {
    pub fn open(path: &str, sync: bool) -> Result<Cask> {
        info!("Opening database: {:?}", &path);
        let mut log = Log::open(path, sync)?;
        let mut index = Index::new();

        let mut sequence = 0;

        for file_id in log.files() {
            let mut f = |hint: Hint| {
                if hint.sequence > sequence {
                    sequence = hint.sequence;
                }

                index.update(hint, file_id);
            };

            match log.hints(file_id)? {
                Some(hints) => {
                    for hint in hints {
                        f(hint?);
                    }
                }
                None => {
                    for hint in log.recreate_hints(file_id)? {
                        f(hint?);
                    }
                }
            };
        }

        info!("Opened database: {:?}", &path);
        info!("Current sequence number: {:?}", sequence);

        let cask = Cask {
            path: log.path.clone(),
            dropped: Arc::new(AtomicBool::new(false)),
            inner: Arc::new(RwLock::new(CaskInner {
                                            current_sequence: sequence + 1,
                                            log: log,
                                            index: index,
                                        })),
            compaction: Arc::new(Mutex::new(())),
        };

        let caskt = cask.clone();
        thread::spawn(move || loop {
                          thread::sleep(Duration::new(COMPACTION_CHECK_FREQUENCY, 0));

                          info!("Compaction thread wake up");

                          if caskt.dropped.load(Ordering::SeqCst) {
                              info!("Cask has been dropped, background compaction \
                                     thread is exiting");
                              break;
                          }

                          if let Err(err) = caskt.compact() {
                              warn!("Error during compaction: {}", err);
                          }
                      });

        Ok(cask)
    }

    fn compact_files_aux(&self, files: &[u32]) -> Result<(Vec<u32>, Vec<u32>)> {
        let active_file_id = {
            self.inner.read().unwrap().log.active_file_id
        };

        let compacted_files_hints = files
            .iter()
            .flat_map(|&file_id| {
                if active_file_id.is_some() && active_file_id.unwrap() == file_id {
                    None
                } else {
                    self.inner
                        .read()
                        .unwrap()
                        .log
                        .hints(file_id)
                        .ok() // FIXME: log the error?
                        .and_then(|hints| hints.map(|h| (file_id, h)))
                }
            });

        let mut compacted_files = Vec::new();
        let mut new_files = Vec::new();
        let mut deletes = HashMap::new();

        let mut log_writer = {
            // FIXME: turn into error
            self.inner.read().unwrap().log.writer()
        };

        for (file_id, hints) in compacted_files_hints {
            let mut inserts = Vec::new();

            for hint in hints {
                let hint = hint?;
                let inner = self.inner.read().unwrap();
                let index_entry = inner.index.get(&*hint.key);

                if hint.deleted {
                    if index_entry.is_none() {
                        match deletes.entry(hint.key.to_vec()) {
                            HashMapEntry::Occupied(mut o) => {
                                if *o.get() < hint.sequence {
                                    o.insert(hint.sequence);
                                }
                            }
                            HashMapEntry::Vacant(e) => {
                                e.insert(hint.sequence);
                            }
                        }
                    }
                } else if index_entry.is_some() && index_entry.unwrap().sequence == hint.sequence {
                    inserts.push(hint)
                }
            }

            for hint in inserts {
                // FIXME: turn into error
                let log = &self.inner.read().unwrap().log;
                let log_write = log_writer.write(&log.read_entry(file_id, hint.entry_pos)?)?;

                if let LogWrite::NewFile(file_id) = log_write {
                    new_files.push(file_id);
                }
            }

            compacted_files.push(file_id);
        }

        for (key, sequence) in deletes {
            log_writer.write(&Entry::deleted(sequence, key))?;
        }

        Ok((compacted_files, new_files))
    }

    fn compact_files(&self, files: &[u32]) -> Result<()> {
        info!("Compacting data files: {:?}", files);

        let (ref compacted_files, ref new_files) = self.compact_files_aux(files)?;

        for &file_id in new_files {
            let hints = {
                self.inner.read().unwrap().log.hints(file_id)?
            };

            if let Some(hints) = hints {
                for hint in hints {
                    let hint = hint?;
                    self.inner.write().unwrap().index.update(hint, file_id);
                }
            };
        }

        self.inner
            .write()
            .unwrap()
            .index
            .stats
            .remove_files(compacted_files);

        self.inner
            .write()
            .unwrap()
            .log
            .swap_files(compacted_files, new_files)?;

        // FIXME: print files not compacted
        info!("Finished compacting data files: {:?} into: {:?}",
              compacted_files,
              new_files);

        Ok(())
    }

    pub fn compact(&self) -> Result<()> {
        let _ = self.compaction.lock().unwrap();

        let active_file_id = {
            self.inner.read().unwrap().log.active_file_id
        };

        let file_stats = {
            self.inner.read().unwrap().index.stats.file_stats()
        };

        let mut files = BTreeSet::new();
        let mut triggered = false;

        for (file_id, fragmentation, dead_bytes) in file_stats {
            if active_file_id.is_some() && file_id == active_file_id.unwrap() {
                continue;
            }

            if !triggered {
                if fragmentation >= FRAGMENTATION_TRIGGER {
                    info!("File {} has fragmentation factor of {}%, triggered compaction",
                          file_id,
                          fragmentation * 100.0);
                    triggered = true;
                    files.insert(file_id);
                } else if dead_bytes >= DEAD_BYTES_TRIGGER {
                    if !files.contains(&file_id) {
                        info!("File {} has {} dead bytes, triggered compaction",
                              file_id,
                              dead_bytes);
                        triggered = true;
                        files.insert(file_id);
                    }
                }
            }

            if fragmentation >= FRAGMENTATION_THRESHOLD {
                if !files.contains(&file_id) {
                    info!("File {} has fragmentation factor of {}%, adding for compaction",
                          file_id,
                          fragmentation * 100.0);
                    files.insert(file_id);
                }
            } else if dead_bytes >= DEAD_BYTES_THRESHOLD {
                if !files.contains(&file_id) {
                    info!("File {} has {} dead bytes, adding for compaction",
                          file_id,
                          dead_bytes);
                    files.insert(file_id);
                }
            }

            if !files.contains(&file_id) {
                let file_size = {
                    self.inner.read().unwrap().log.file_size(file_id).ok()
                };

                if let Some(file_size) = file_size {
                    if file_size <= SMALL_FILE_THRESHOLD {
                        info!("File {} has total size of {} bytes, adding for compaction",
                              file_id,
                              file_size);
                        files.insert(file_id);
                    }
                };
            }
        }

        if triggered {
            let files: Vec<_> = files.into_iter().collect();
            self.compact_files(&files)?;
        } else {
            if files.is_empty() {
                info!("No files eligible for compaction")
            } else {
                info!("Compaction of files {:?} aborted due to missing trigger",
                      &files);
            }
        }

        Ok(())
    }

    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Vec<u8>>> {
        self.inner.read().unwrap().get(key.as_ref())
    }

    pub fn put<K: Into<Vec<u8>>, V: AsRef<[u8]>>(&self, key: K, value: V) -> Result<()> {
        self.inner.write().unwrap().put(key.into(), value.as_ref())
    }

    pub fn delete<K: AsRef<[u8]>>(&self, key: K) -> Result<()> {
        self.inner.write().unwrap().delete(key.as_ref())
    }
}

impl Drop for Cask {
    fn drop(&mut self) {
        self.dropped.store(true, Ordering::SeqCst);
    }
}
<|endoftext|>


<commit_before>use primitives::{Consumed, Parser, ParseError, ParseResult, Error, State, Stream};
use combinator::{FnParser, many, Many, Map, ParserExt};

macro_rules! impl_char_parser {
    ($name: ident ($($ty_var: ident),*), $inner_type: ty) => {
    #[derive(Clone)]
    pub struct $name<I $(,$ty_var)*>($inner_type)
        where I: Stream<Item=char> $(, $ty_var : Parser<Input=I>)*;
    impl <I $(,$ty_var)*> Parser for $name<I $(,$ty_var)*>
        where I: Stream<Item=char> $(, $ty_var : Parser<Input=I>)* {
        type Input = I;
        type Output = <$inner_type as Parser>::Output;
        fn parse_state(&mut self, input: State<<Self as Parser>::Input>) -> ParseResult<<Self as Parser>::Output, <Self as Parser>::Input> {
            self.0.parse_state(input)
        }
    }
}
}

///Parses any character
pub fn any_char<I>(input: State<I>) -> ParseResult<char, I>
    where I: Stream<Item=char> {
    input.uncons_char()
}

#[derive(Clone)]
pub struct Satisfy<I, Pred> { pred: Pred }

impl <I, Pred> Parser for Satisfy<I, Pred>
    where I: Stream<Item=char>, Pred: FnMut(char) -> bool {

    type Input = I;
    type Output = char;
    fn parse_state(&mut self, input: State<I>) -> ParseResult<char, I> {
        match input.clone().uncons_char() {
            Ok((c, s)) => {
                if (self.pred)(c) { Ok((c, s)) }
                else {
                    Err(Consumed::Empty(ParseError::new(input.position, Error::Unexpected(c))))
                }
            }
            Err(err) => Err(err)
        }
    }
}

///Parses a character and succeeds depending on the result of `pred`
///
/// ```
/// # extern crate "parser-combinators" as pc;
/// # use pc::*;
/// # fn main() {
/// let result = satisfy(|c| c == '!')
///     .parse("!")
///     .map(|x| x.0);
/// assert_eq!(result, Ok('!'));
/// # }
/// ```
pub fn satisfy<I, Pred>(pred: Pred) -> Satisfy<I, Pred>
    where I: Stream, Pred: FnMut(char) -> bool {
    Satisfy { pred: pred }
}

///Parses a digit from a stream containing characters
pub fn digit<I>() -> FnParser<I, char, fn (State<I>) -> ParseResult<char, I>>
        where I: Stream<Item=char> {
    fn digit_<I>(input: State<I>) -> ParseResult<char, I>
        where I: Stream<Item=char> {
        match input.clone().uncons_char() {
            Ok((c, rest)) => {
                if c.is_digit(10) { Ok((c, rest)) }
                else {
                    Err(Consumed::Empty(ParseError::new(input.position, Error::Message("Expected digit".to_string()))))
                }
            }
            Err(err) => Err(err)
        }
    }
    FnParser(digit_ as fn (_) -> _)
}

///Parses whitespace
pub fn space<I>() -> Satisfy<I, fn (char) -> bool>
    where I: Stream {
    satisfy(CharExt::is_whitespace as fn (char) -> bool)
}
impl_char_parser! { Spaces(), Many<Vec<()>, Map<Satisfy<I, fn (char) -> bool>, fn (char), ()>> }
///Skips over zero or more spaces
pub fn spaces<I>() -> Spaces<I>
    where I: Stream<Item=char> {
    Spaces(many(space().map(static_fn!((_, char) -> () { () }))))
}

#[derive(Clone)]
pub struct StringP<'a, I> { s: &'a str }
impl <'a, I> Parser for StringP<'a, I>
    where I: Stream<Item=char> {
    type Input = I;
    type Output = &'a str;
    fn parse_state(&mut self, input: State<I>) -> ParseResult<&'a str, I> {
        let start = input.position;
        let mut input = Consumed::Empty(input);
        for (i, c) in self.s.chars().enumerate() {
            match input.combine(|input| input.uncons_char()) {
                Ok((other, rest)) => {
                    if c != other {
                        let error = ParseError::new(start, Error::Expected(self.s.to_string()));
                        return Err(if i == 0 { Consumed::Empty(error) } else { Consumed::Consumed(error) });
                    }
                    input = rest;
                }
                Err(error) => {
                    return error.combine(move |mut error| {
                        error.position = start;
                        Err(if i == 0 { Consumed::Empty(error) } else { Consumed::Consumed(error) })
                    })
                }
            }
        }
        Ok((self.s, input))
    }
}

///Parses the string `s`
///
/// ```
/// # extern crate "parser-combinators" as pc;
/// # use pc::*;
/// # fn main() {
/// let result = string("rust")
///     .parse("rust")
///     .map(|x| x.0);
/// assert_eq!(result, Ok("rust"));
/// # }
/// ```
pub fn string<I>(s: &str) -> StringP<I>
    where I: Stream {
    StringP { s: s }
}
<commit_msg>Hide the digit and space parsers behind a newtype and specialize char parsers<commit_after>use primitives::{Consumed, Parser, ParseError, ParseResult, Error, State, Stream};
use combinator::{FnParser, many, Many, Map, ParserExt};

macro_rules! impl_char_parser {
    ($name: ident ($($ty_var: ident),*), $inner_type: ty) => {
    #[derive(Clone)]
    pub struct $name<I $(,$ty_var)*>($inner_type)
        where I: Stream<Item=char> $(, $ty_var : Parser<Input=I>)*;
    impl <I $(,$ty_var)*> Parser for $name<I $(,$ty_var)*>
        where I: Stream<Item=char> $(, $ty_var : Parser<Input=I>)* {
        type Input = I;
        type Output = <$inner_type as Parser>::Output;
        fn parse_state(&mut self, input: State<<Self as Parser>::Input>) -> ParseResult<<Self as Parser>::Output, <Self as Parser>::Input> {
            self.0.parse_state(input)
        }
    }
}
}

///Parses any character
pub fn any_char<I>(input: State<I>) -> ParseResult<char, I>
    where I: Stream<Item=char> {
    input.uncons_char()
}

#[derive(Clone)]
pub struct Satisfy<I, Pred> { pred: Pred }

impl <I, Pred> Parser for Satisfy<I, Pred>
    where I: Stream<Item=char>, Pred: FnMut(char) -> bool {

    type Input = I;
    type Output = char;
    fn parse_state(&mut self, input: State<I>) -> ParseResult<char, I> {
        match input.clone().uncons_char() {
            Ok((c, s)) => {
                if (self.pred)(c) { Ok((c, s)) }
                else {
                    Err(Consumed::Empty(ParseError::new(input.position, Error::Unexpected(c))))
                }
            }
            Err(err) => Err(err)
        }
    }
}

///Parses a character and succeeds depending on the result of `pred`
///
/// ```
/// # extern crate "parser-combinators" as pc;
/// # use pc::*;
/// # fn main() {
/// let result = satisfy(|c| c == '!')
///     .parse("!")
///     .map(|x| x.0);
/// assert_eq!(result, Ok('!'));
/// # }
/// ```
pub fn satisfy<I, Pred>(pred: Pred) -> Satisfy<I, Pred>
    where I: Stream<Item=char>, Pred: FnMut(char) -> bool {
    Satisfy { pred: pred }
}

impl_char_parser! { Digit(), FnParser<I, char, fn (State<I>) -> ParseResult<char, I>> }
///Parses a digit from a stream containing characters
pub fn digit<I>() -> Digit<I>
        where I: Stream<Item=char> {
    fn digit_<I>(input: State<I>) -> ParseResult<char, I>
        where I: Stream<Item=char> {
        match input.clone().uncons_char() {
            Ok((c, rest)) => {
                if c.is_digit(10) { Ok((c, rest)) }
                else {
                    Err(Consumed::Empty(ParseError::new(input.position, Error::Message("Expected digit".to_string()))))
                }
            }
            Err(err) => Err(err)
        }
    }
    Digit(FnParser(digit_ as fn (_) -> _))
}

impl_char_parser! { Space(), Satisfy<I, fn (char) -> bool> }
///Parses whitespace
pub fn space<I>() -> Space<I>
    where I: Stream<Item=char> {
    Space(satisfy(CharExt::is_whitespace as fn (char) -> bool))
}

impl_char_parser! { Spaces(), Many<Vec<()>, Map<Space<I>, fn (char), ()>> }
///Skips over zero or more spaces
pub fn spaces<I>() -> Spaces<I>
    where I: Stream<Item=char> {
    Spaces(many(space().map(static_fn!((_, char) -> () { () }))))
}

#[derive(Clone)]
pub struct StringP<'a, I> { s: &'a str }
impl <'a, I> Parser for StringP<'a, I>
    where I: Stream<Item=char> {
    type Input = I;
    type Output = &'a str;
    fn parse_state(&mut self, input: State<I>) -> ParseResult<&'a str, I> {
        let start = input.position;
        let mut input = Consumed::Empty(input);
        for (i, c) in self.s.chars().enumerate() {
            match input.combine(|input| input.uncons_char()) {
                Ok((other, rest)) => {
                    if c != other {
                        let error = ParseError::new(start, Error::Expected(self.s.to_string()));
                        return Err(if i == 0 { Consumed::Empty(error) } else { Consumed::Consumed(error) });
                    }
                    input = rest;
                }
                Err(error) => {
                    return error.combine(move |mut error| {
                        error.position = start;
                        Err(if i == 0 { Consumed::Empty(error) } else { Consumed::Consumed(error) })
                    })
                }
            }
        }
        Ok((self.s, input))
    }
}

///Parses the string `s`
///
/// ```
/// # extern crate "parser-combinators" as pc;
/// # use pc::*;
/// # fn main() {
/// let result = string("rust")
///     .parse("rust")
///     .map(|x| x.0);
/// assert_eq!(result, Ok("rust"));
/// # }
/// ```
pub fn string<I>(s: &str) -> StringP<I>
    where I: Stream<Item=char> {
    StringP { s: s }
}
<|endoftext|>


<commit_before>use libc::c_void;

use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use driver::cmd::Args;
use error::diag::Diagnostic;

use ast;
use ast::map::Map;
use class::{Class, ClassId};
use gc::Gc;
use interner::*;
use jit::fct::JitFct;
use jit::map::CodeMap;
use jit::stub::Stub;
use mem::Ptr;
use sym::*;
use sym::Sym::*;
use ty::BuiltinType;

pub static mut ctxt_ptr: Option<Ptr> = None;

pub fn get_ctxt() -> &'static Context<'static, 'static> {
    unsafe {
        &*(ctxt_ptr.unwrap().raw() as *const Context)
    }
}

pub struct Context<'a, 'ast> where 'ast: 'a {
    pub args: &'a Args,
    pub interner: &'a Interner,
    pub map: &'a Map<'ast>,
    pub ast: &'ast ast::Ast,
    pub diag: RefCell<Diagnostic>,
    pub sym: RefCell<SymTable>,
    pub classes: Vec<Box<Class<'ast>>>, // stores all class definitions
    pub cls_defs: HashMap<ast::NodeId, ClassId>, // points from AST class to ClassId
    pub fct_defs: HashMap<ast::NodeId, FctId>, // points from AST function definition
                                                 // node id to FctId
    pub fcts: Vec<Arc<Mutex<Fct<'ast>>>>, // stores all function definitions
    pub code_map: Mutex<CodeMap>, // stores all compiled functions
    pub gc: Mutex<Gc>, // garbage collector
}

impl<'a, 'ast> Context<'a, 'ast> {
    pub fn new(args: &'a Args, interner: &'a Interner,
           map: &'a Map<'ast>, ast: &'ast ast::Ast) -> Context<'a, 'ast> {
        Context {
            args: args,
            classes: Vec::new(),
            cls_defs: HashMap::new(),
            interner: interner,
            gc: Mutex::new(Gc::new()),
            map: map,
            ast: ast,
            diag: RefCell::new(Diagnostic::new()),
            sym: RefCell::new(SymTable::new()),
            fct_defs: HashMap::new(),
            fcts: Vec::new(),
            code_map: Mutex::new(CodeMap::new()),
        }
    }

    pub fn add_fct(&mut self, mut fct: Fct<'ast>) -> FctId {
        let name = fct.name;
        let fctid = FctId(self.fcts.len());

        fct.id = fctid;

        if fct.kind.is_src() {
            assert!(self.fct_defs.insert(fct.ast().id, fctid).is_none());
        }

        self.fcts.push(Arc::new(Mutex::new(fct)));

        fctid
    }

    pub fn add_function(&mut self, mut fct: Fct<'ast>) -> Result<FctId, Sym> {
        let name = fct.name;
        let fctid = self.add_fct(fct);

        let mut sym = self.sym.borrow_mut();

        match sym.get(name) {
            Some(sym) => Err(sym),
            None => {
                assert!(sym.insert(name, SymFct(fctid)).is_none());

                Ok(fctid)
            }
        }
    }

    pub fn fct_by_id<F, R>(&self, id: FctId, f: F) -> R where F: FnOnce(&Fct<'ast>) -> R {
        let fct = self.fcts[id.0].clone();
        let fctxt = fct.lock().unwrap();

        f(&fctxt)
    }

    pub fn fct_by_id_mut<F, R>(&self, id: FctId, f: F) -> R where F: FnOnce(&mut Fct<'ast>) -> R {
        let fct = self.fcts[id.0].clone();
        let mut fctxt = fct.lock().unwrap();

        f(&mut fctxt)
    }

    pub fn cls_by_id(&self, id: ClassId) -> &Class<'ast> {
        &self.classes[id.0]
    }

    pub fn cls_by_id_mut(&mut self, id: ClassId) -> &mut Class<'ast> {
        &mut self.classes[id.0]
    }

    pub fn fct_by_node_id_mut<F, R>(&self, id: ast::NodeId, f: F) -> R where F: FnOnce(&mut Fct<'ast>) -> R {
        let fct_id = *self.fct_defs.get(&id).unwrap();

        self.fct_by_id_mut(fct_id, f)
    }

    pub fn fct_by_node_id<F, R>(&self, id: ast::NodeId, f: F) -> R where
                     F: FnOnce(&Fct<'ast>) -> R {
        let fct_id = *self.fct_defs.get(&id).unwrap();

        self.fct_by_id(fct_id, f)
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct FctId(pub usize);

#[derive(Debug)]
pub struct Fct<'ast> {
    pub id: FctId,
    pub name: Name,
    pub owner_class: Option<ClassId>,
    pub params_types: Vec<BuiltinType>,
    pub return_type: BuiltinType,
    pub ctor: bool,

    pub kind: FctKind<'ast>,
}

impl<'ast> Fct<'ast> {
    pub fn ast(&self) -> &'ast ast::Function {
        self.src().ast
    }

    pub fn src(&self) -> &FctSrc<'ast> {
        self.kind.src()
    }

    pub fn src_mut(&mut self) -> &mut FctSrc<'ast> {
        self.kind.src_mut()
    }

    pub fn var_by_node_id(&self, id: ast::NodeId) -> &Var {
        let varid = *self.src().defs.get(&id).unwrap();

        &self.src().vars[varid.0]
    }

    pub fn var_by_node_id_mut(&mut self, id: ast::NodeId) -> &mut Var {
        let varid = *self.src().defs.get(&id).unwrap();

        &mut self.src_mut().vars[varid.0]
    }
}

#[derive(Debug)]
pub enum FctKind<'ast> {
    Source(FctSrc<'ast>), Builtin(Ptr), Intrinsic
}

impl<'ast> FctKind<'ast> {
    pub fn is_src(&self) -> bool {
        match *self {
            FctKind::Source(_) => true,
            _ => false
        }
    }

    pub fn src(&self) -> &FctSrc<'ast> {
        match *self {
            FctKind::Source(ref ast_info) => ast_info,
            _ => unreachable!()
        }
    }

    pub fn src_mut(&mut self) -> &mut FctSrc<'ast> {
        match *self {
            FctKind::Source(ref mut ast_info) => ast_info,
            _ => unreachable!()
        }
    }
}

#[derive(Debug)]
pub struct FctSrc<'ast> {
    pub ast: &'ast ast::Function,
    pub types: HashMap<ast::NodeId, BuiltinType>, // maps expression to type
    pub calls: HashMap<ast::NodeId, FctId>, // maps function call to FctId
    pub defs: HashMap<ast::NodeId, VarId>, // points to the definition of variable from its usage
    pub tempsize: i32, // size of temporary variables on stack
    pub localsize: i32, // size of local variables on stack
    pub leaf: bool, // false if fct calls other functions
    pub vars: Vec<Var>, // variables in functions
    pub always_returns: bool, // true if function is always exited via return statement
                              // false if function execution could reach the closing } of this function
    pub jit_fct: Option<JitFct>, // compile function
    pub stub: Option<Stub> // compiler stub
}

impl<'ast> FctSrc<'ast> {
    pub fn stacksize(&self) -> i32 {
        self.tempsize + self.localsize
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct VarId(pub usize);

#[derive(Debug)]
pub struct Var {
    pub id: VarId,
    pub name: Name,
    pub data_type: BuiltinType,
    pub node_id: ast::NodeId,
    pub offset: i32,
}
<commit_msg>added FctGen<commit_after>use libc::c_void;

use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use driver::cmd::Args;
use error::diag::Diagnostic;

use ast;
use ast::map::Map;
use class::{Class, ClassId};
use gc::Gc;
use interner::*;
use jit::fct::JitFct;
use jit::map::CodeMap;
use jit::stub::Stub;
use mem::Ptr;
use sym::*;
use sym::Sym::*;
use ty::BuiltinType;

pub static mut ctxt_ptr: Option<Ptr> = None;

pub fn get_ctxt() -> &'static Context<'static, 'static> {
    unsafe {
        &*(ctxt_ptr.unwrap().raw() as *const Context)
    }
}

pub struct Context<'a, 'ast> where 'ast: 'a {
    pub args: &'a Args,
    pub interner: &'a Interner,
    pub map: &'a Map<'ast>,
    pub ast: &'ast ast::Ast,
    pub diag: RefCell<Diagnostic>,
    pub sym: RefCell<SymTable>,
    pub classes: Vec<Box<Class<'ast>>>, // stores all class definitions
    pub cls_defs: HashMap<ast::NodeId, ClassId>, // points from AST class to ClassId
    pub fct_defs: HashMap<ast::NodeId, FctId>, // points from AST function definition
                                                 // node id to FctId
    pub fcts: Vec<Arc<Mutex<Fct<'ast>>>>, // stores all function definitions
    pub code_map: Mutex<CodeMap>, // stores all compiled functions
    pub gc: Mutex<Gc>, // garbage collector
}

impl<'a, 'ast> Context<'a, 'ast> {
    pub fn new(args: &'a Args, interner: &'a Interner,
           map: &'a Map<'ast>, ast: &'ast ast::Ast) -> Context<'a, 'ast> {
        Context {
            args: args,
            classes: Vec::new(),
            cls_defs: HashMap::new(),
            interner: interner,
            gc: Mutex::new(Gc::new()),
            map: map,
            ast: ast,
            diag: RefCell::new(Diagnostic::new()),
            sym: RefCell::new(SymTable::new()),
            fct_defs: HashMap::new(),
            fcts: Vec::new(),
            code_map: Mutex::new(CodeMap::new()),
        }
    }

    pub fn add_fct(&mut self, mut fct: Fct<'ast>) -> FctId {
        let name = fct.name;
        let fctid = FctId(self.fcts.len());

        fct.id = fctid;

        if fct.kind.is_src() {
            assert!(self.fct_defs.insert(fct.ast().id, fctid).is_none());
        }

        self.fcts.push(Arc::new(Mutex::new(fct)));

        fctid
    }

    pub fn add_function(&mut self, mut fct: Fct<'ast>) -> Result<FctId, Sym> {
        let name = fct.name;
        let fctid = self.add_fct(fct);

        let mut sym = self.sym.borrow_mut();

        match sym.get(name) {
            Some(sym) => Err(sym),
            None => {
                assert!(sym.insert(name, SymFct(fctid)).is_none());

                Ok(fctid)
            }
        }
    }

    pub fn fct_by_id<F, R>(&self, id: FctId, f: F) -> R where F: FnOnce(&Fct<'ast>) -> R {
        let fct = self.fcts[id.0].clone();
        let fctxt = fct.lock().unwrap();

        f(&fctxt)
    }

    pub fn fct_by_id_mut<F, R>(&self, id: FctId, f: F) -> R where F: FnOnce(&mut Fct<'ast>) -> R {
        let fct = self.fcts[id.0].clone();
        let mut fctxt = fct.lock().unwrap();

        f(&mut fctxt)
    }

    pub fn cls_by_id(&self, id: ClassId) -> &Class<'ast> {
        &self.classes[id.0]
    }

    pub fn cls_by_id_mut(&mut self, id: ClassId) -> &mut Class<'ast> {
        &mut self.classes[id.0]
    }

    pub fn fct_by_node_id_mut<F, R>(&self, id: ast::NodeId, f: F) -> R where F: FnOnce(&mut Fct<'ast>) -> R {
        let fct_id = *self.fct_defs.get(&id).unwrap();

        self.fct_by_id_mut(fct_id, f)
    }

    pub fn fct_by_node_id<F, R>(&self, id: ast::NodeId, f: F) -> R where
                     F: FnOnce(&Fct<'ast>) -> R {
        let fct_id = *self.fct_defs.get(&id).unwrap();

        self.fct_by_id(fct_id, f)
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct FctId(pub usize);

#[derive(Debug)]
pub struct Fct<'ast> {
    pub id: FctId,
    pub name: Name,
    pub owner_class: Option<ClassId>,
    pub params_types: Vec<BuiltinType>,
    pub return_type: BuiltinType,
    pub ctor: bool,

    pub kind: FctKind<'ast>,
}

impl<'ast> Fct<'ast> {
    pub fn ast(&self) -> &'ast ast::Function {
        self.src().ast
    }

    pub fn src(&self) -> &FctSrc<'ast> {
        self.kind.src()
    }

    pub fn src_mut(&mut self) -> &mut FctSrc<'ast> {
        self.kind.src_mut()
    }

    pub fn var_by_node_id(&self, id: ast::NodeId) -> &Var {
        let varid = *self.src().defs.get(&id).unwrap();

        &self.src().vars[varid.0]
    }

    pub fn var_by_node_id_mut(&mut self, id: ast::NodeId) -> &mut Var {
        let varid = *self.src().defs.get(&id).unwrap();

        &mut self.src_mut().vars[varid.0]
    }
}

#[derive(Debug)]
pub enum FctKind<'ast> {
    Source(FctSrc<'ast>), Gen(FctGen) Builtin(Ptr), Intrinsic
}

impl<'ast> FctKind<'ast> {
    pub fn is_src(&self) -> bool {
        match *self {
            FctKind::Source(_) => true,
            _ => false
        }
    }

    pub fn src(&self) -> &FctSrc<'ast> {
        match *self {
            FctKind::Source(ref ast_info) => ast_info,
            _ => unreachable!()
        }
    }

    pub fn src_mut(&mut self) -> &mut FctSrc<'ast> {
        match *self {
            FctKind::Source(ref mut ast_info) => ast_info,
            _ => unreachable!()
        }
    }
}

#[derive(Debug)]
pub struct FctGen {
    pub jit_fct: Option<JitFct>, // compile function
    pub stub: Option<Stub> // compiler stub
}

#[derive(Debug)]
pub struct FctSrc<'ast> {
    pub ast: &'ast ast::Function,
    pub types: HashMap<ast::NodeId, BuiltinType>, // maps expression to type
    pub calls: HashMap<ast::NodeId, FctId>, // maps function call to FctId
    pub defs: HashMap<ast::NodeId, VarId>, // points to the definition of variable from its usage
    pub tempsize: i32, // size of temporary variables on stack
    pub localsize: i32, // size of local variables on stack
    pub leaf: bool, // false if fct calls other functions
    pub vars: Vec<Var>, // variables in functions
    pub always_returns: bool, // true if function is always exited via return statement
                              // false if function execution could reach the closing } of this function
    pub jit_fct: Option<JitFct>, // compile function
    pub stub: Option<Stub> // compiler stub
}

impl<'ast> FctSrc<'ast> {
    pub fn stacksize(&self) -> i32 {
        self.tempsize + self.localsize
    }
}

#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct VarId(pub usize);

#[derive(Debug)]
pub struct Var {
    pub id: VarId,
    pub name: Name,
    pub data_type: BuiltinType,
    pub node_id: ast::NodeId,
    pub offset: i32,
}
<|endoftext|>