<commit_before>#![feature(quote, plugin_registrar, rustc_private, plugin, custom_attribute)]
#![crate_type = "dylib"]

extern crate syntax;
extern crate rustc_plugin;

use rustc_plugin::Registry;
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension};
use syntax::codemap::Span;
use syntax::ast;
use syntax::parse::token;
use std::boxed::Box;
use syntax::feature_gate::AttributeType;

#[plugin_registrar]
pub fn registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("async"),
                                  SyntaxExtension::MultiModifier(Box::new(async_attribute)));
}

fn async_attribute(cx: &mut ExtCtxt,
                   span: Span,
                   _: &ast::MetaItem,
                   annotable: Annotatable)
                   -> Annotatable {
//
//    match annotable {
//        Annotatable::Item(item) => {
//            Annotatable::Item(quote_item!(cx,
//            fn foo() -> u32 {
//                let sum = 2 + 2;
//                println!("{}", sum);
//                sum
//            })
//                                  .unwrap())
//        }
//        other => {
//            cx.span_err(span, "did not get item");
//            other.clone()
//        }
//    };

    Annotatable::Item(quote_item!(cx,
            fn foo() -> u32 {
                let sum = 2 + 2;
                println!("{}", sum);
                sum
            })
                          .unwrap())
}<commit_msg>Function statements accessible for modification<commit_after>#![feature(quote, plugin_registrar, rustc_private, plugin, custom_attribute)]
#![crate_type = "dylib"]

extern crate syntax;
extern crate rustc_plugin;

use rustc_plugin::Registry;
use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxExtension};
use syntax::codemap::Span;
use syntax::parse::token;
use std::boxed::Box;
use syntax::feature_gate::AttributeType;
use syntax::ast;
use syntax::ptr::P;
use syntax::ext::build::AstBuilder;
use std::vec::Vec;

#[plugin_registrar]
pub fn registrar(reg: &mut Registry) {
    reg.register_syntax_extension(token::intern("async"),
                                  SyntaxExtension::MultiModifier(Box::new(async_attribute)));
}

fn async_attribute(cx: &mut ExtCtxt,
                   span: Span,
                   _: &ast::MetaItem,
                   annotable: Annotatable)
                   -> Annotatable {

    // The function item
    let item = annotable.clone().expect_item();

    if let ast::Item_::ItemFn(dec, unsafety, constness, abi, generics, block) = item.node.clone() {
        let block = cx.block(block.span, generate_callbacks(block.stmts.clone()), block.expr.clone());

        Annotatable::Item(cx.item(item.span.clone(),
                                  item.ident.clone(),
                                  item.attrs.clone(),
                                  ast::Item_::ItemFn(dec, unsafety, constness, abi, generics, block)))
    } else {
        cx.span_err(span, "The async annotation only works on functions.");
        annotable
    }
}

fn generate_callbacks(stmts: Vec<P<ast::Stmt>>) -> Vec<P<ast::Stmt>> {
    vec![]
}
<|endoftext|>


<commit_before>#![feature(associated_consts)]
#![feature(drain)]

extern crate uuid;
extern crate typed_arena;
mod js_types;
mod alloc;

use std::collections::hash_set::HashSet;
use std::ptr::null_mut;
use std::rc::Rc;
use uuid::Uuid;

use alloc::scope::Scope;
use js_types::js_type::JsVar;

pub struct ScopeManager {
    root_scope: Rc<Scope>,
    curr_scope: *mut Rc<Scope>,
}

impl ScopeManager {
    pub fn new<F>(callback: F) -> ScopeManager where F: Fn() -> HashSet<Uuid> + 'static {
        let scope = Rc::new(Scope::new(callback));
        let mut mgr =
            ScopeManager {
                root_scope: scope,
                curr_scope: null_mut(),
            };
        mgr.curr_scope = &mut (mgr.root_scope) as *mut Rc<Scope>;
        mgr
    }

    pub fn push_scope<F>(&mut self, callback: F) where F: Fn() -> HashSet<Uuid> + 'static {
        unsafe {
            let weak_clone = Rc::downgrade(&*self.curr_scope.clone());
            self.curr_scope =
                Rc::get_mut(&mut *self.curr_scope)
                    .unwrap()
                    .add_child(Scope::as_child(weak_clone, callback)) as *mut Rc<Scope>;
        }
    }

    pub fn pop_scope(&mut self) {
        let parent = unsafe { (*self.curr_scope).parent.clone() };
        if let Some(parent) = parent {
            if let Some(mut scope) = parent.upgrade() {
                // Pop old scope
                let num_children = scope.children.len();
                Rc::get_mut(&mut scope).unwrap().children.remove(num_children - 1);
                // Set curr_scope to old scope's parent
                self.curr_scope = &mut scope as *mut Rc<Scope>;
            }
        } else {
            panic!("Tried to pop to parent scope, but parent did not exist!");
        }
    }

    pub fn alloc(&mut self, var: JsVar) -> Uuid {
        unsafe { Rc::get_mut(&mut *self.curr_scope).unwrap().alloc(var) }
    }

    pub fn load(&self, uuid: Uuid) -> Option<JsVar> {
        unsafe { (*self.curr_scope).get_var_copy(&uuid) }
    }

    pub fn store(&mut self, var: JsVar) -> bool {
        unsafe { Rc::get_mut(&mut *self.curr_scope).unwrap().update_var(var) }
    }
}

pub fn init_gc<F>(callback: F) -> ScopeManager
    where F: Fn() -> HashSet<Uuid> + 'static {
    ScopeManager::new(callback)
}

<commit_msg>Make load return a Result and store return an Option<Uuid><commit_after>#![feature(associated_consts)]
#![feature(drain)]

extern crate uuid;
extern crate typed_arena;
mod js_types;
mod alloc;

use std::collections::hash_set::HashSet;
use std::ptr::null_mut;
use std::rc::Rc;
use uuid::Uuid;

use alloc::scope::Scope;
use js_types::js_type::JsVar;

pub struct ScopeManager {
    root_scope: Rc<Scope>,
    curr_scope: *mut Rc<Scope>,
}

impl ScopeManager {
    pub fn new<F>(callback: F) -> ScopeManager where F: Fn() -> HashSet<Uuid> + 'static {
        let scope = Rc::new(Scope::new(callback));
        let mut mgr =
            ScopeManager {
                root_scope: scope,
                curr_scope: null_mut(),
            };
        mgr.curr_scope = &mut (mgr.root_scope) as *mut Rc<Scope>;
        mgr
    }

    pub fn push_scope<F>(&mut self, callback: F) where F: Fn() -> HashSet<Uuid> + 'static {
        unsafe {
            let weak_clone = Rc::downgrade(&*self.curr_scope.clone());
            self.curr_scope =
                Rc::get_mut(&mut *self.curr_scope)
                    .unwrap()
                    .add_child(Scope::as_child(weak_clone, callback)) as *mut Rc<Scope>;
        }
    }

    pub fn pop_scope(&mut self) {
        let parent = unsafe { (*self.curr_scope).parent.clone() };
        if let Some(parent) = parent {
            if let Some(mut scope) = parent.upgrade() {
                // Pop old scope
                let num_children = scope.children.len();
                Rc::get_mut(&mut scope).unwrap().children.remove(num_children - 1);
                // Set curr_scope to old scope's parent
                self.curr_scope = &mut scope as *mut Rc<Scope>;
            }
        } else {
            panic!("Tried to pop to parent scope, but parent did not exist!");
        }
    }

    pub fn alloc(&mut self, var: JsVar) -> Uuid {
        unsafe { Rc::get_mut(&mut *self.curr_scope).unwrap().alloc(var) }
    }

    pub fn load(&self, uuid: Uuid) -> Result<JsVar, Err> {
        unsafe { (*self.curr_scope).get_var_copy(&uuid) }
        .ok_or("Lookup of uuid {} failed!", uuid)
    }

    pub fn store(&mut self, var: JsVar) -> Option<Uuid> {
        if !unsafe { Rc::get_mut(&mut *self.curr_scope).unwrap().update_var(var) } {
            Some(self.alloc(var))
        } else { None }
    }
}

pub fn init_gc<F>(callback: F) -> ScopeManager
    where F: Fn() -> HashSet<Uuid> + 'static {
    ScopeManager::new(callback)
}

<|endoftext|>


<commit_before>#![warn(missing_docs)]
//! Abstraction over how a game can work

#[macro_use]
extern crate glium;
extern crate time;
extern crate image;
#[macro_use]
extern crate quick_error;

mod draw;
mod logic;
mod vector;

pub use draw::{Graphics, Texture, Drawer, Draw};
pub use logic::{GameManager, Update, FrameInfo, VirtualKeyCode};
pub use vector::{Vector2, FloatVector};

/// Result type for `korome::TextureError`
pub type TextureResult<T> = Result<T, TextureError>;
/// Result type for `glium::DrawError`
pub type DrawResult<T> = Result<T, glium::DrawError>;

quick_error! {
    /// Wraps together all errors that can occur creating `Texture`s
    #[derive(Debug)]
    pub enum TextureError{
        /// A `glium::texture::TextureCreationError`
        TextureCreationError(err: glium::texture::TextureCreationError){
            from()
            cause(err)
            description("texture creation error")
        }
        /// An `image::ImageError`
        ImageError(err: image::ImageError){
            from()
            cause(err)
            description(err.description())
        }
        /// A `glium::vertex::buffer::CreationError`
        BufferCreationError(err: glium::vertex::BufferCreationError){
            from()
            cause(err)
            description(err.description())
        }
        /// An `std::io::Error`
        IoError(err: std::io::Error){
            from()
            cause(err)
            description(err.description())
        }
    }
}
<commit_msg>Re-export `MouseButton`<commit_after>#![warn(missing_docs)]
//! Abstraction over how a game can work

#[macro_use]
extern crate glium;
extern crate time;
extern crate image;
#[macro_use]
extern crate quick_error;

mod draw;
mod logic;
mod vector;

pub use draw::{Graphics, Texture, Drawer, Draw};
pub use logic::{GameManager, Update, FrameInfo, VirtualKeyCode, MouseButton};
pub use vector::{Vector2, FloatVector};

/// Result type for `korome::TextureError`
pub type TextureResult<T> = Result<T, TextureError>;
/// Result type for `glium::DrawError`
pub type DrawResult<T> = Result<T, glium::DrawError>;

quick_error! {
    /// Wraps together all errors that can occur creating `Texture`s
    #[derive(Debug)]
    pub enum TextureError{
        /// A `glium::texture::TextureCreationError`
        TextureCreationError(err: glium::texture::TextureCreationError){
            from()
            cause(err)
            description("texture creation error")
        }
        /// An `image::ImageError`
        ImageError(err: image::ImageError){
            from()
            cause(err)
            description(err.description())
        }
        /// A `glium::vertex::buffer::CreationError`
        BufferCreationError(err: glium::vertex::BufferCreationError){
            from()
            cause(err)
            description(err.description())
        }
        /// An `std::io::Error`
        IoError(err: std::io::Error){
            from()
            cause(err)
            description(err.description())
        }
    }
}
<|endoftext|>


<commit_before>use std::time::*;
use std::hash::Hash;
use lru_cache::LruCache;

// A cache that allows for per-value timeout invalidation

pub enum LRUEntry<T> {
    Present(T),
    Expired,
    Vacant,
}

pub struct LRU<K: Hash + Eq, T> {
    cache: LruCache<K, (T, Instant, Duration)>,
}

impl<K, T> LRU<K, T>
    where K: Hash + Eq
{
    pub fn new(limit: usize) -> LRU<K, T> {
        LRU { cache: LruCache::new(limit) }
    }

    pub fn insert(&mut self, key: K, val: T, lifespan: Duration) {
        self.cache.insert(key, (val, Instant::now(), lifespan));
    }

    pub fn get(&mut self, key: &K) -> LRUEntry<&T> {
        let (val, then, lifespan) = match self.cache.get_mut(key) {
            Some(&mut (ref val, then, lifespan)) => (val as *const T, then, lifespan),
            None => return LRUEntry::Vacant,
        };

        if Self::is_timed_out(then, lifespan) {
            self.cache.remove(key);
            // self.cache.take(key)
            LRUEntry::Expired
        } else {
            LRUEntry::Present(unsafe { &*val })
        }
    }

    fn is_timed_out(then: Instant, dur: Duration) -> bool {
        let age = then.elapsed();
        if age >= dur {
            false
        } else {
            true
        }
    }
}
<commit_msg>Update lru.rs<commit_after>use std::time::*;
use std::hash::Hash;
use lru_cache::LruCache;

// A cache that allows for per-value timeout invalidation

pub enum LRUEntry<T> {
    Present(T),
    Expired,
    Vacant,
}

pub struct LRU<K: Hash + Eq, T> {
    cache: LruCache<K, (T, Instant, Duration)>,
    hits: u128,
    misses: u128,
    exp_misses: u128,
}

impl<K, T> LRU<K, T>
    where K: Hash + Eq
{
    pub fn new(limit: usize) -> LRU<K, T> {
        LRU {
            cache: LruCache::new(limit),
            hits: 1,
            misses: 1,
            exp_misses: 1,
        }
    }

    pub fn insert(&mut self, key: K, val: T, lifespan: Duration) {
        self.cache.insert(key, (val, Instant::now(), lifespan));
    }

    pub fn get(&mut self, key: &K) -> LRUEntry<&T> {
        let (val, then, lifespan) = match self.cache.get_mut(key) {
            Some(&mut (ref val, then, lifespan)) => (val as *const T, then, lifespan),
            None => {self.misses += 1; return LRUEntry::Vacant},
        };

        if Self::is_timed_out(then, lifespan) {
            self.cache.remove(key);
            // self.cache.take(key)
            self.exp_misses += 1;
            LRUEntry::Expired
        } else {
            self.hits += 1;
            LRUEntry::Present(unsafe { &*val })
        }
    }

    fn is_timed_out(then: Instant, dur: Duration) -> bool {
        let age = then.elapsed();
        if age >= dur {
            false
        } else {
            true
        }
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(convert)]

//! #Introduction
//! This crate focuses on geting system information.
//!
//! For now it supports Linux, Mac OS X and Windows.
//! And now it can get information of kernel/cpu/memory/disk/load/hostname and so on.
//!

use std::ffi;
use std::str;
use std::io::Read;
use std::fs::File;
use std::sync::{Once, ONCE_INIT};
use std::mem;
use std::convert::AsRef;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    /// Average load within one minite.
    pub one: f64,
    /// Average load within five minites.
    pub five: f64,
    /// Average load within fifteen minites.
    pub fifteen: f64
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    /// Total physical memory.
    pub total: u64,
    pub free:  u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached:  u64,

    /// Total swap memory.
    pub swap_total: u64,
    pub swap_free:  u64
}

/// System momory information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64
}

extern {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, String> {
    static mut OS_TYPE: &'static str = "";
    static OS_TYPE_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            OS_TYPE_INIT.call_once(|| {
                if cfg!(target_os = "linux") {
                    let mut f = File::open("/proc/sys/kernel/ostype").unwrap();
                    let mut s = String::new();
                    let _ = f.read_to_string(&mut s).unwrap();
                    s.pop();  // pop '\n'
                    OS_TYPE = mem::copy_lifetime(OS_TYPE, s.as_ref());
                    mem::forget(s);
                } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
                    OS_TYPE = str::from_utf8(ffi::CStr::from_ptr(get_os_type())
                                   .to_bytes()).unwrap()
                }
            });
            Ok(OS_TYPE.to_string())
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, String> {
    static mut OS_RELEASE: &'static str = "";
    static OS_RELEASE_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            OS_RELEASE_INIT.call_once(|| {
                if cfg!(target_os = "linux") {
                    let mut f = File::open("/proc/sys/kernel/osrelease").unwrap();
                    let mut s = String::new();
                    let _ = f.read_to_string(&mut s).unwrap();
                    s.pop();
                    OS_RELEASE = std::mem::copy_lifetime(OS_RELEASE, s.as_ref());
                    mem::forget(s);
                } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
                    OS_RELEASE = str::from_utf8(
                        ffi::CStr::from_ptr(get_os_release()).to_bytes()).unwrap();
                }
            });
            Ok(OS_RELEASE.to_string())
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, String> {
    static mut CPU_NUM: u32 = 0;
    static CPU_NUM_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            CPU_NUM_INIT.call_once(|| {
                CPU_NUM = get_cpu_num();
            });
            Ok(CPU_NUM)
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, String> {
    static mut CPU_SPEED: u64 = 0;
    static CPU_SPEED_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            CPU_SPEED_INIT.call_once(|| {
                CPU_SPEED = if cfg!(target_os = "linux") {
                    // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
                    let mut f = File::open("/proc/cpuinfo").unwrap();
                    let mut s = String::new();
                    let _ = f.read_to_string(&mut s).unwrap();
                    let sr: &str = s.as_ref();
                    let mut lines = sr.split('\n');
                    for _ in 0..7 {
                        lines.next();
                    }
                    let mut words = lines.next().unwrap().split(':');
                    words.next();
                    let s = words.next().unwrap().trim().trim_right_matches('\n');
                    s.parse::<f64>().unwrap() as u64
                } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
                    get_cpu_speed()
                } else { 0 };
            });
            Ok(CPU_SPEED)
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let sr: &str = s.as_ref();
        let mut words = sr.split(' ');
        let one = words.next().unwrap().parse::<f64>().unwrap();
        let five = words.next().unwrap().parse::<f64>().unwrap();
        let fifteen = words.next().unwrap().parse::<f64>().unwrap();
        Ok(LoadAvg { one: one, five: five, fifteen: fifteen} )
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        Ok({
            let sr: &str = s.as_ref();
            let mut words = sr.splitn(3, ' ');
            for _ in 0..3 {
                words.next();
            }
            let mut words = words.next().unwrap().split('/');
            words.next();
            let mut words = words.next().unwrap().split(' ');
            words.next().unwrap().parse::<u64>().unwrap()
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err("Unsupported system".to_string())
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    let mut line = line.splitn(1, ' ');
    line.next();
    line.next().unwrap().trim_left().split(' ').next().unwrap()
        .parse::<u64>().unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/meminfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let sr: &str = s.as_ref();
        let mut lines = sr.split('\n');
        let total = get_mem_num(lines.next().unwrap());
        let free = get_mem_num(lines.next().unwrap());
        let avail = get_mem_num(lines.next().unwrap());
        let buffers = get_mem_num(lines.next().unwrap());
        let cached = get_mem_num(lines.next().unwrap());
        let swap_total = {
            for _ in 0..9 {
                lines.next();
            }
            get_mem_num(lines.next().unwrap())
        };
        let swap_free = get_mem_num(lines.next().unwrap());

        Ok(MemInfo{ total: total, free: free, avail: avail,
                    buffers: buffers, cached: cached,
                    swap_total: swap_total, swap_free: swap_free})
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, String> {
    if cfg!(target_os = "linux") ||
        cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe { get_disk_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get hostname.
pub fn hostname() -> Result<String, String> {
    use std::process::Command;
    if cfg!(unix) {
        let output = match Command::new("hostname").output() {
            Ok(o)  => o,
            Err(e) => return Err(format!("failed to execute process: {}", e)),
        };
        let mut s = String::from_utf8(output.stdout).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else if cfg!(windows) {
        let output = match Command::new("hostname").output() {
            Ok(o)  => o,
            Err(e) => return Err(format!("failed to execute process: {}", e)),
        };
        let mut s = String::from_utf8(output.stdout).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else {
        Err("Unsupported system".to_string())
    }
}

<commit_msg>fix as_ref<commit_after>#![feature(core)]
#![feature(convert)]

//! #Introduction
//! This crate focuses on geting system information.
//!
//! For now it supports Linux, Mac OS X and Windows.
//! And now it can get information of kernel/cpu/memory/disk/load/hostname and so on.
//!

use std::ffi;
use std::str;
use std::io::Read;
use std::fs::File;
use std::sync::{Once, ONCE_INIT};
use std::mem;
use std::convert::AsRef;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    /// Average load within one minite.
    pub one: f64,
    /// Average load within five minites.
    pub five: f64,
    /// Average load within fifteen minites.
    pub fifteen: f64
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    /// Total physical memory.
    pub total: u64,
    pub free:  u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached:  u64,

    /// Total swap memory.
    pub swap_total: u64,
    pub swap_free:  u64
}

/// System momory information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64
}

extern {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, String> {
    static mut OS_TYPE: &'static str = "";
    static OS_TYPE_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            OS_TYPE_INIT.call_once(|| {
                if cfg!(target_os = "linux") {
                    let mut f = File::open("/proc/sys/kernel/ostype").unwrap();
                    let mut s = String::new();
                    let _ = f.read_to_string(&mut s).unwrap();
                    s.pop();  // pop '\n'
                    OS_TYPE = mem::copy_lifetime(OS_TYPE, s.as_ref());
                    mem::forget(s);
                } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
                    OS_TYPE = str::from_utf8(ffi::CStr::from_ptr(get_os_type())
                                   .to_bytes()).unwrap()
                }
            });
            Ok(OS_TYPE.to_string())
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, String> {
    static mut OS_RELEASE: &'static str = "";
    static OS_RELEASE_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            OS_RELEASE_INIT.call_once(|| {
                if cfg!(target_os = "linux") {
                    let mut f = File::open("/proc/sys/kernel/osrelease").unwrap();
                    let mut s = String::new();
                    let _ = f.read_to_string(&mut s).unwrap();
                    s.pop();
                    OS_RELEASE = std::mem::copy_lifetime(OS_RELEASE, s.as_ref());
                    mem::forget(s);
                } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
                    OS_RELEASE = str::from_utf8(
                        ffi::CStr::from_ptr(get_os_release()).to_bytes()).unwrap();
                }
            });
            Ok(OS_RELEASE.to_string())
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, String> {
    static mut CPU_NUM: u32 = 0;
    static CPU_NUM_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            CPU_NUM_INIT.call_once(|| {
                CPU_NUM = get_cpu_num();
            });
            Ok(CPU_NUM)
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, String> {
    static mut CPU_SPEED: u64 = 0;
    static CPU_SPEED_INIT: Once = ONCE_INIT;
    if cfg!(unix) || cfg!(windows) {
        unsafe {
            CPU_SPEED_INIT.call_once(|| {
                CPU_SPEED = if cfg!(target_os = "linux") {
                    // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
                    let mut f = File::open("/proc/cpuinfo").unwrap();
                    let mut s = String::new();
                    let _ = f.read_to_string(&mut s).unwrap();
                    let mut lines = s.split('\n');
                    for _ in 0..7 {
                        lines.next();
                    }
                    let mut words = lines.next().unwrap().split(':');
                    words.next();
                    let s = words.next().unwrap().trim().trim_right_matches('\n');
                    s.parse::<f64>().unwrap() as u64
                } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
                    get_cpu_speed()
                } else { 0 };
            });
            Ok(CPU_SPEED)
        }
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut words = s.split(' ');
        let one = words.next().unwrap().parse::<f64>().unwrap();
        let five = words.next().unwrap().parse::<f64>().unwrap();
        let fifteen = words.next().unwrap().parse::<f64>().unwrap();
        Ok(LoadAvg { one: one, five: five, fifteen: fifteen} )
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        Ok({
            let mut words = s.splitn(3, ' ');
            for _ in 0..3 {
                words.next();
            }
            let mut words = words.next().unwrap().split('/');
            words.next();
            let mut words = words.next().unwrap().split(' ');
            words.next().unwrap().parse::<u64>().unwrap()
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err("Unsupported system".to_string())
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    let mut line = line.splitn(1, ' ');
    line.next();
    line.next().unwrap().trim_left().split(' ').next().unwrap()
        .parse::<u64>().unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/meminfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.split('\n');
        let total = get_mem_num(lines.next().unwrap());
        let free = get_mem_num(lines.next().unwrap());
        let avail = get_mem_num(lines.next().unwrap());
        let buffers = get_mem_num(lines.next().unwrap());
        let cached = get_mem_num(lines.next().unwrap());
        let swap_total = {
            for _ in 0..9 {
                lines.next();
            }
            get_mem_num(lines.next().unwrap())
        };
        let swap_free = get_mem_num(lines.next().unwrap());

        Ok(MemInfo{ total: total, free: free, avail: avail,
                    buffers: buffers, cached: cached,
                    swap_total: swap_total, swap_free: swap_free})
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, String> {
    if cfg!(target_os = "linux") ||
        cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe { get_disk_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get hostname.
pub fn hostname() -> Result<String, String> {
    use std::process::Command;
    if cfg!(unix) {
        let output = match Command::new("hostname").output() {
            Ok(o)  => o,
            Err(e) => return Err(format!("failed to execute process: {}", e)),
        };
        let mut s = String::from_utf8(output.stdout).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else if cfg!(windows) {
        let output = match Command::new("hostname").output() {
            Ok(o)  => o,
            Err(e) => return Err(format!("failed to execute process: {}", e)),
        };
        let mut s = String::from_utf8(output.stdout).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else {
        Err("Unsupported system".to_string())
    }
}

<|endoftext|>


<commit_before>#[link(name = "OpenCL",
       vers = "0.1",
       uuid = "26bc3ce8-7720-41a7-9280-c522285f2e70")];
#[crate_type = "lib"];
#[feature(macro_rules)];
#[feature(globs)];

extern mod std;

#[nolink]
#[link_args = "-framework OpenCL"]
#[cfg(target_os = "macos")]
extern { }

#[nolink]
#[link_args = "-lOpenCL"]
#[cfg(target_os = "linux")]
extern { }

pub mod CL;
pub mod error;
pub mod hl;
pub mod vector;
<commit_msg>add managed_boxes flag<commit_after>#[link(name = "OpenCL",
       vers = "0.1",
       uuid = "26bc3ce8-7720-41a7-9280-c522285f2e70")];
#[crate_type = "lib"];
#[feature(macro_rules)];
#[feature(globs)];
#[feature(managed_boxes)];

extern mod std;

#[nolink]
#[link_args = "-framework OpenCL"]
#[cfg(target_os = "macos")]
extern { }

#[nolink]
#[link_args = "-lOpenCL"]
#[cfg(target_os = "linux")]
extern { }

pub mod CL;
pub mod error;
pub mod hl;
pub mod vector;
<|endoftext|>


<commit_before>//! The `Connect` trait, and supporting types.
//!
//! This module contains:
//!
//! - A default [`HttpConnector`](HttpConnector) that does DNS resolution and
//!   establishes connections over TCP.
//! - The [`Connect`](Connect) trait and related types to build custom connectors.
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::mem;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;

use futures::{Future, Poll, Async};
use futures::future::{Executor, ExecuteError};
use futures::sync::oneshot;
use futures_cpupool::{Builder as CpuPoolBuilder};
use http::Uri;
use http::uri::Scheme;
use net2::TcpBuilder;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio::reactor::Handle;
use tokio::net::{TcpStream, ConnectFuture};

use super::dns;
use self::http_connector::HttpConnectorBlockingTask;

/// Connect to a destination, returning an IO transport.
///
/// A connector receives a [`Destination`](Destination) describing how a
/// connection should be estabilished, and returns a `Future` of the
/// ready connection.
pub trait Connect: Send + Sync {
    /// The connected IO Stream.
    type Transport: AsyncRead + AsyncWrite + Send + 'static;
    /// An error occured when trying to connect.
    type Error: Into<Box<StdError + Send + Sync>>;
    /// A Future that will resolve to the connected Transport.
    type Future: Future<Item=(Self::Transport, Connected), Error=Self::Error> + Send;
    /// Connect to a destination.
    fn connect(&self, dst: Destination) -> Self::Future;
}

/// A set of properties to describe where and how to try to connect.
#[derive(Debug)]
pub struct Destination {
    //pub(super) alpn: Alpn,
    pub(super) uri: Uri,
}

/// Extra information about the connected transport.
///
/// This can be used to inform recipients about things like if ALPN
/// was used, or if connected to an HTTP proxy.
#[derive(Debug)]
pub struct Connected {
    //alpn: Alpn,
    pub(super) is_proxied: bool,
}

/*TODO: when HTTP1 Upgrades to H2 are added, this will be needed
#[derive(Debug)]
pub(super) enum Alpn {
    Http1,
    //H2,
    //Http1OrH2
}
*/

impl Destination {
    /// Get the protocol scheme.
    #[inline]
    pub fn scheme(&self) -> &str {
        self.uri
            .scheme_part()
            .expect("destination uri has scheme")
            .as_str()
    }

    /// Get the hostname.
    #[inline]
    pub fn host(&self) -> &str {
        self.uri
            .host()
            .expect("destination uri has host")
    }

    /// Get the port, if specified.
    #[inline]
    pub fn port(&self) -> Option<u16> {
        self.uri.port()
    }

    /*
    /// Returns whether this connection must negotiate HTTP/2 via ALPN.
    pub fn must_h2(&self) -> bool {
        match self.alpn {
            Alpn::Http1 => false,
            Alpn::H2 => true,
        }
    }
    */
}

impl Connected {
    /// Create new `Connected` type with empty metadata.
    pub fn new() -> Connected {
        Connected {
            //alpn: Alpn::Http1,
            is_proxied: false,
        }
    }

    /// Set whether the connected transport is to an HTTP proxy.
    ///
    /// This setting will affect if HTTP/1 requests written on the transport
    /// will have the request-target in absolute-form or origin-form (such as
    /// `GET http://hyper.rs/guide HTTP/1.1` or `GET /guide HTTP/1.1`).
    ///
    /// Default is `false`.
    pub fn proxy(mut self, is_proxied: bool) -> Connected {
        self.is_proxied = is_proxied;
        self
    }

    /*
    /// Set that the connected transport negotiated HTTP/2 as it's
    /// next protocol.
    pub fn h2(mut self) -> Connected {
        self.alpn = Alpn::H2;
        self
    }
    */
}

fn connect(addr: &SocketAddr, handle: &Option<Handle>) -> io::Result<ConnectFuture> {
    if let Some(ref handle) = *handle {
        let builder = match addr {
            &SocketAddr::V4(_) => TcpBuilder::new_v4()?,
            &SocketAddr::V6(_) => TcpBuilder::new_v6()?,
        };

        if cfg!(windows) {
            // Windows requires a socket be bound before calling connect
            let any: SocketAddr = match addr {
                &SocketAddr::V4(_) => {
                    ([0, 0, 0, 0], 0).into()
                },
                &SocketAddr::V6(_) => {
                    ([0, 0, 0, 0, 0, 0, 0, 0], 0).into()
                }
            };
            builder.bind(any)?;
        }

        Ok(TcpStream::connect_std(builder.to_tcp_stream()?, addr, handle))
    } else {
        Ok(TcpStream::connect(addr))
    }
}

/// A connector for the `http` scheme.
///
/// Performs DNS resolution in a thread pool, and then connects over TCP.
#[derive(Clone)]
pub struct HttpConnector {
    executor: HttpConnectExecutor,
    enforce_http: bool,
    handle: Option<Handle>,
    keep_alive_timeout: Option<Duration>,
}

impl HttpConnector {
    /// Construct a new HttpConnector.
    ///
    /// Takes number of DNS worker threads.
    #[inline]
    pub fn new(threads: usize) -> HttpConnector {
        HttpConnector::new_with_handle_opt(threads, None)
    }

    /// Construct a new HttpConnector with a specific Tokio handle.
    pub fn new_with_handle(threads: usize, handle: Handle) -> HttpConnector {
        HttpConnector::new_with_handle_opt(threads, Some(handle))
    }

    fn new_with_handle_opt(threads: usize, handle: Option<Handle>) -> HttpConnector {
        let pool = CpuPoolBuilder::new()
            .name_prefix("hyper-dns")
            .pool_size(threads)
            .create();
        HttpConnector::new_with_executor(pool, handle)
    }

    /// Construct a new HttpConnector.
    ///
    /// Takes an executor to run blocking tasks on.
    pub fn new_with_executor<E: 'static>(executor: E, handle: Option<Handle>) -> HttpConnector
        where E: Executor<HttpConnectorBlockingTask> + Send + Sync
    {
        HttpConnector {
            executor: HttpConnectExecutor(Arc::new(executor)),
            enforce_http: true,
            handle,
            keep_alive_timeout: None,
        }
    }

    /// Option to enforce all `Uri`s have the `http` scheme.
    ///
    /// Enabled by default.
    #[inline]
    pub fn enforce_http(&mut self, is_enforced: bool) {
        self.enforce_http = is_enforced;
    }

    /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
    ///
    /// If `None`, the option will not be set.
    ///
    /// Default is `None`.
    #[inline]
    pub fn set_keepalive(&mut self, dur: Option<Duration>) {
        self.keep_alive_timeout = dur;
    }
}

impl fmt::Debug for HttpConnector {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("HttpConnector")
            .finish()
    }
}

impl Connect for HttpConnector {
    type Transport = TcpStream;
    type Error = io::Error;
    type Future = HttpConnecting;

    fn connect(&self, dst: Destination) -> Self::Future {
        trace!("Http::connect({:?})", dst.uri);

        if self.enforce_http {
            if dst.uri.scheme_part() != Some(&Scheme::HTTP) {
                return invalid_url(InvalidUrl::NotHttp, &self.handle);
            }
        } else if dst.uri.scheme_part().is_none() {
            return invalid_url(InvalidUrl::MissingScheme, &self.handle);
        }

        let host = match dst.uri.host() {
            Some(s) => s,
            None => return invalid_url(InvalidUrl::MissingAuthority, &self.handle),
        };
        let port = match dst.uri.port() {
            Some(port) => port,
            None => if dst.uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 },
        };

        HttpConnecting {
            state: State::Lazy(self.executor.clone(), host.into(), port),
            handle: self.handle.clone(),
            keep_alive_timeout: self.keep_alive_timeout,
        }
    }
}

#[inline]
fn invalid_url(err: InvalidUrl, handle: &Option<Handle>) -> HttpConnecting {
    HttpConnecting {
        state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, err))),
        handle: handle.clone(),
        keep_alive_timeout: None,
    }
}

#[derive(Debug, Clone, Copy)]
enum InvalidUrl {
    MissingScheme,
    NotHttp,
    MissingAuthority,
}

impl fmt::Display for InvalidUrl {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

impl StdError for InvalidUrl {
    fn description(&self) -> &str {
        match *self {
            InvalidUrl::MissingScheme => "invalid URL, missing scheme",
            InvalidUrl::NotHttp => "invalid URL, scheme must be http",
            InvalidUrl::MissingAuthority => "invalid URL, missing domain",
        }
    }
}

/// A Future representing work to connect to a URL.
#[must_use = "futures do nothing unless polled"]
pub struct HttpConnecting {
    state: State,
    handle: Option<Handle>,
    keep_alive_timeout: Option<Duration>,
}

enum State {
    Lazy(HttpConnectExecutor, String, u16),
    Resolving(oneshot::SpawnHandle<dns::IpAddrs, io::Error>),
    Connecting(ConnectingTcp),
    Error(Option<io::Error>),
}

impl Future for HttpConnecting {
    type Item = (TcpStream, Connected);
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            let state;
            match self.state {
                State::Lazy(ref executor, ref mut host, port) => {
                    // If the host is already an IP addr (v4 or v6),
                    // skip resolving the dns and start connecting right away.
                    if let Some(addrs) = dns::IpAddrs::try_parse(host, port) {
                        state = State::Connecting(ConnectingTcp {
                            addrs: addrs,
                            current: None
                        })
                    } else {
                        let host = mem::replace(host, String::new());
                        let work = dns::Work::new(host, port);
                        state = State::Resolving(oneshot::spawn(work, executor));
                    }
                },
                State::Resolving(ref mut future) => {
                    match try!(future.poll()) {
                        Async::NotReady => return Ok(Async::NotReady),
                        Async::Ready(addrs) => {
                            state = State::Connecting(ConnectingTcp {
                                addrs: addrs,
                                current: None,
                            })
                        }
                    };
                },
                State::Connecting(ref mut c) => {
                    let sock = try_ready!(c.poll(&self.handle));

                    if let Some(dur) = self.keep_alive_timeout {
                        sock.set_keepalive(Some(dur))?;
                    }

                    return Ok(Async::Ready((sock, Connected::new())));
                },
                State::Error(ref mut e) => return Err(e.take().expect("polled more than once")),
            }
            self.state = state;
        }
    }
}

impl fmt::Debug for HttpConnecting {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("HttpConnecting")
    }
}

struct ConnectingTcp {
    addrs: dns::IpAddrs,
    current: Option<ConnectFuture>,
}

impl ConnectingTcp {
    // not a Future, since passing a &Handle to poll
    fn poll(&mut self, handle: &Option<Handle>) -> Poll<TcpStream, io::Error> {
        let mut err = None;
        loop {
            if let Some(ref mut current) = self.current {
                match current.poll() {
                    Ok(ok) => return Ok(ok),
                    Err(e) => {
                        trace!("connect error {:?}", e);
                        err = Some(e);
                        if let Some(addr) = self.addrs.next() {
                            debug!("connecting to {}", addr);
                            *current = connect(&addr, handle)?;
                            continue;
                        }
                    }
                }
            } else if let Some(addr) = self.addrs.next() {
                debug!("connecting to {}", addr);
                self.current = Some(connect(&addr, handle)?);
                continue;
            }

            return Err(err.take().expect("missing connect error"));
        }
    }
}

// Make this Future unnameable outside of this crate.
mod http_connector {
    use super::*;
    // Blocking task to be executed on a thread pool.
    pub struct HttpConnectorBlockingTask {
        pub(super) work: oneshot::Execute<dns::Work>
    }

    impl fmt::Debug for HttpConnectorBlockingTask {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.pad("HttpConnectorBlockingTask")
        }
    }

    impl Future for HttpConnectorBlockingTask {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            self.work.poll()
        }
    }
}

#[derive(Clone)]
struct HttpConnectExecutor(Arc<Executor<HttpConnectorBlockingTask> + Send + Sync>);

impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor {
    fn execute(&self, future: oneshot::Execute<dns::Work>) -> Result<(), ExecuteError<oneshot::Execute<dns::Work>>> {
        self.0.execute(HttpConnectorBlockingTask { work: future })
            .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work))
    }
}

#[cfg(test)]
mod tests {
    #![allow(deprecated)]
    use std::io;
    use futures::Future;
    use super::{Connect, Destination, HttpConnector};

    #[test]
    fn test_errors_missing_authority() {
        let uri = "/foo/bar?baz".parse().unwrap();
        let dst = Destination {
            uri,
        };
        let connector = HttpConnector::new(1);

        assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
    }

    #[test]
    fn test_errors_enforce_http() {
        let uri = "https://example.domain/foo/bar?baz".parse().unwrap();
        let dst = Destination {
            uri,
        };
        let connector = HttpConnector::new(1);

        assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
    }

    #[test]
    fn test_errors_missing_scheme() {
        let uri = "example.domain".parse().unwrap();
        let dst = Destination {
            uri,
        };
        let connector = HttpConnector::new(1);

        assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
    }
}
<commit_msg>feat(client): add support to set SO_NODELAY on client HTTP sockets<commit_after>//! The `Connect` trait, and supporting types.
//!
//! This module contains:
//!
//! - A default [`HttpConnector`](HttpConnector) that does DNS resolution and
//!   establishes connections over TCP.
//! - The [`Connect`](Connect) trait and related types to build custom connectors.
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::mem;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;

use futures::{Future, Poll, Async};
use futures::future::{Executor, ExecuteError};
use futures::sync::oneshot;
use futures_cpupool::{Builder as CpuPoolBuilder};
use http::Uri;
use http::uri::Scheme;
use net2::TcpBuilder;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio::reactor::Handle;
use tokio::net::{TcpStream, ConnectFuture};

use super::dns;
use self::http_connector::HttpConnectorBlockingTask;

/// Connect to a destination, returning an IO transport.
///
/// A connector receives a [`Destination`](Destination) describing how a
/// connection should be estabilished, and returns a `Future` of the
/// ready connection.
pub trait Connect: Send + Sync {
    /// The connected IO Stream.
    type Transport: AsyncRead + AsyncWrite + Send + 'static;
    /// An error occured when trying to connect.
    type Error: Into<Box<StdError + Send + Sync>>;
    /// A Future that will resolve to the connected Transport.
    type Future: Future<Item=(Self::Transport, Connected), Error=Self::Error> + Send;
    /// Connect to a destination.
    fn connect(&self, dst: Destination) -> Self::Future;
}

/// A set of properties to describe where and how to try to connect.
#[derive(Debug)]
pub struct Destination {
    //pub(super) alpn: Alpn,
    pub(super) uri: Uri,
}

/// Extra information about the connected transport.
///
/// This can be used to inform recipients about things like if ALPN
/// was used, or if connected to an HTTP proxy.
#[derive(Debug)]
pub struct Connected {
    //alpn: Alpn,
    pub(super) is_proxied: bool,
}

/*TODO: when HTTP1 Upgrades to H2 are added, this will be needed
#[derive(Debug)]
pub(super) enum Alpn {
    Http1,
    //H2,
    //Http1OrH2
}
*/

impl Destination {
    /// Get the protocol scheme.
    #[inline]
    pub fn scheme(&self) -> &str {
        self.uri
            .scheme_part()
            .expect("destination uri has scheme")
            .as_str()
    }

    /// Get the hostname.
    #[inline]
    pub fn host(&self) -> &str {
        self.uri
            .host()
            .expect("destination uri has host")
    }

    /// Get the port, if specified.
    #[inline]
    pub fn port(&self) -> Option<u16> {
        self.uri.port()
    }

    /*
    /// Returns whether this connection must negotiate HTTP/2 via ALPN.
    pub fn must_h2(&self) -> bool {
        match self.alpn {
            Alpn::Http1 => false,
            Alpn::H2 => true,
        }
    }
    */
}

impl Connected {
    /// Create new `Connected` type with empty metadata.
    pub fn new() -> Connected {
        Connected {
            //alpn: Alpn::Http1,
            is_proxied: false,
        }
    }

    /// Set whether the connected transport is to an HTTP proxy.
    ///
    /// This setting will affect if HTTP/1 requests written on the transport
    /// will have the request-target in absolute-form or origin-form (such as
    /// `GET http://hyper.rs/guide HTTP/1.1` or `GET /guide HTTP/1.1`).
    ///
    /// Default is `false`.
    pub fn proxy(mut self, is_proxied: bool) -> Connected {
        self.is_proxied = is_proxied;
        self
    }

    /*
    /// Set that the connected transport negotiated HTTP/2 as it's
    /// next protocol.
    pub fn h2(mut self) -> Connected {
        self.alpn = Alpn::H2;
        self
    }
    */
}

fn connect(addr: &SocketAddr, handle: &Option<Handle>) -> io::Result<ConnectFuture> {
    if let Some(ref handle) = *handle {
        let builder = match addr {
            &SocketAddr::V4(_) => TcpBuilder::new_v4()?,
            &SocketAddr::V6(_) => TcpBuilder::new_v6()?,
        };

        if cfg!(windows) {
            // Windows requires a socket be bound before calling connect
            let any: SocketAddr = match addr {
                &SocketAddr::V4(_) => {
                    ([0, 0, 0, 0], 0).into()
                },
                &SocketAddr::V6(_) => {
                    ([0, 0, 0, 0, 0, 0, 0, 0], 0).into()
                }
            };
            builder.bind(any)?;
        }

        Ok(TcpStream::connect_std(builder.to_tcp_stream()?, addr, handle))
    } else {
        Ok(TcpStream::connect(addr))
    }
}

/// A connector for the `http` scheme.
///
/// Performs DNS resolution in a thread pool, and then connects over TCP.
#[derive(Clone)]
pub struct HttpConnector {
    executor: HttpConnectExecutor,
    enforce_http: bool,
    handle: Option<Handle>,
    keep_alive_timeout: Option<Duration>,
    nodelay: bool,
}

impl HttpConnector {
    /// Construct a new HttpConnector.
    ///
    /// Takes number of DNS worker threads.
    #[inline]
    pub fn new(threads: usize) -> HttpConnector {
        HttpConnector::new_with_handle_opt(threads, None)
    }

    /// Construct a new HttpConnector with a specific Tokio handle.
    pub fn new_with_handle(threads: usize, handle: Handle) -> HttpConnector {
        HttpConnector::new_with_handle_opt(threads, Some(handle))
    }

    fn new_with_handle_opt(threads: usize, handle: Option<Handle>) -> HttpConnector {
        let pool = CpuPoolBuilder::new()
            .name_prefix("hyper-dns")
            .pool_size(threads)
            .create();
        HttpConnector::new_with_executor(pool, handle)
    }

    /// Construct a new HttpConnector.
    ///
    /// Takes an executor to run blocking tasks on.
    pub fn new_with_executor<E: 'static>(executor: E, handle: Option<Handle>) -> HttpConnector
        where E: Executor<HttpConnectorBlockingTask> + Send + Sync
    {
        HttpConnector {
            executor: HttpConnectExecutor(Arc::new(executor)),
            enforce_http: true,
            handle,
            keep_alive_timeout: None,
            nodelay: false,
        }
    }

    /// Option to enforce all `Uri`s have the `http` scheme.
    ///
    /// Enabled by default.
    #[inline]
    pub fn enforce_http(&mut self, is_enforced: bool) {
        self.enforce_http = is_enforced;
    }

    /// Set that all sockets have `SO_KEEPALIVE` set with the supplied duration.
    ///
    /// If `None`, the option will not be set.
    ///
    /// Default is `None`.
    #[inline]
    pub fn set_keepalive(&mut self, dur: Option<Duration>) {
        self.keep_alive_timeout = dur;
    }

    /// Set that all sockets have `SO_NODELAY` set to the supplied value `nodelay`.
    ///
    /// Default is `false`.
    #[inline]
    pub fn set_nodelay(&mut self, nodelay: bool) {
        self.nodelay = nodelay;
    }
}

impl fmt::Debug for HttpConnector {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("HttpConnector")
            .finish()
    }
}

impl Connect for HttpConnector {
    type Transport = TcpStream;
    type Error = io::Error;
    type Future = HttpConnecting;

    fn connect(&self, dst: Destination) -> Self::Future {
        trace!("Http::connect({:?})", dst.uri);

        if self.enforce_http {
            if dst.uri.scheme_part() != Some(&Scheme::HTTP) {
                return invalid_url(InvalidUrl::NotHttp, &self.handle);
            }
        } else if dst.uri.scheme_part().is_none() {
            return invalid_url(InvalidUrl::MissingScheme, &self.handle);
        }

        let host = match dst.uri.host() {
            Some(s) => s,
            None => return invalid_url(InvalidUrl::MissingAuthority, &self.handle),
        };
        let port = match dst.uri.port() {
            Some(port) => port,
            None => if dst.uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 },
        };

        HttpConnecting {
            state: State::Lazy(self.executor.clone(), host.into(), port),
            handle: self.handle.clone(),
            keep_alive_timeout: self.keep_alive_timeout,
            nodelay: self.nodelay,
        }
    }
}

#[inline]
fn invalid_url(err: InvalidUrl, handle: &Option<Handle>) -> HttpConnecting {
    HttpConnecting {
        state: State::Error(Some(io::Error::new(io::ErrorKind::InvalidInput, err))),
        handle: handle.clone(),
        keep_alive_timeout: None,
        nodelay: false,
    }
}

#[derive(Debug, Clone, Copy)]
enum InvalidUrl {
    MissingScheme,
    NotHttp,
    MissingAuthority,
}

impl fmt::Display for InvalidUrl {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

impl StdError for InvalidUrl {
    fn description(&self) -> &str {
        match *self {
            InvalidUrl::MissingScheme => "invalid URL, missing scheme",
            InvalidUrl::NotHttp => "invalid URL, scheme must be http",
            InvalidUrl::MissingAuthority => "invalid URL, missing domain",
        }
    }
}

/// A Future representing work to connect to a URL.
#[must_use = "futures do nothing unless polled"]
pub struct HttpConnecting {
    state: State,
    handle: Option<Handle>,
    keep_alive_timeout: Option<Duration>,
    nodelay: bool,
}

enum State {
    Lazy(HttpConnectExecutor, String, u16),
    Resolving(oneshot::SpawnHandle<dns::IpAddrs, io::Error>),
    Connecting(ConnectingTcp),
    Error(Option<io::Error>),
}

impl Future for HttpConnecting {
    type Item = (TcpStream, Connected);
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            let state;
            match self.state {
                State::Lazy(ref executor, ref mut host, port) => {
                    // If the host is already an IP addr (v4 or v6),
                    // skip resolving the dns and start connecting right away.
                    if let Some(addrs) = dns::IpAddrs::try_parse(host, port) {
                        state = State::Connecting(ConnectingTcp {
                            addrs: addrs,
                            current: None
                        })
                    } else {
                        let host = mem::replace(host, String::new());
                        let work = dns::Work::new(host, port);
                        state = State::Resolving(oneshot::spawn(work, executor));
                    }
                },
                State::Resolving(ref mut future) => {
                    match try!(future.poll()) {
                        Async::NotReady => return Ok(Async::NotReady),
                        Async::Ready(addrs) => {
                            state = State::Connecting(ConnectingTcp {
                                addrs: addrs,
                                current: None,
                            })
                        }
                    };
                },
                State::Connecting(ref mut c) => {
                    let sock = try_ready!(c.poll(&self.handle));

                    if let Some(dur) = self.keep_alive_timeout {
                        sock.set_keepalive(Some(dur))?;
                    }

                    sock.set_nodelay(self.nodelay)?;

                    return Ok(Async::Ready((sock, Connected::new())));
                },
                State::Error(ref mut e) => return Err(e.take().expect("polled more than once")),
            }
            self.state = state;
        }
    }
}

impl fmt::Debug for HttpConnecting {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("HttpConnecting")
    }
}

struct ConnectingTcp {
    addrs: dns::IpAddrs,
    current: Option<ConnectFuture>,
}

impl ConnectingTcp {
    // not a Future, since passing a &Handle to poll
    fn poll(&mut self, handle: &Option<Handle>) -> Poll<TcpStream, io::Error> {
        let mut err = None;
        loop {
            if let Some(ref mut current) = self.current {
                match current.poll() {
                    Ok(ok) => return Ok(ok),
                    Err(e) => {
                        trace!("connect error {:?}", e);
                        err = Some(e);
                        if let Some(addr) = self.addrs.next() {
                            debug!("connecting to {}", addr);
                            *current = connect(&addr, handle)?;
                            continue;
                        }
                    }
                }
            } else if let Some(addr) = self.addrs.next() {
                debug!("connecting to {}", addr);
                self.current = Some(connect(&addr, handle)?);
                continue;
            }

            return Err(err.take().expect("missing connect error"));
        }
    }
}

// Make this Future unnameable outside of this crate.
mod http_connector {
    use super::*;
    // Blocking task to be executed on a thread pool.
    pub struct HttpConnectorBlockingTask {
        pub(super) work: oneshot::Execute<dns::Work>
    }

    impl fmt::Debug for HttpConnectorBlockingTask {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.pad("HttpConnectorBlockingTask")
        }
    }

    impl Future for HttpConnectorBlockingTask {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            self.work.poll()
        }
    }
}

#[derive(Clone)]
struct HttpConnectExecutor(Arc<Executor<HttpConnectorBlockingTask> + Send + Sync>);

impl Executor<oneshot::Execute<dns::Work>> for HttpConnectExecutor {
    fn execute(&self, future: oneshot::Execute<dns::Work>) -> Result<(), ExecuteError<oneshot::Execute<dns::Work>>> {
        self.0.execute(HttpConnectorBlockingTask { work: future })
            .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work))
    }
}

#[cfg(test)]
mod tests {
    #![allow(deprecated)]
    use std::io;
    use futures::Future;
    use super::{Connect, Destination, HttpConnector};

    #[test]
    fn test_errors_missing_authority() {
        let uri = "/foo/bar?baz".parse().unwrap();
        let dst = Destination {
            uri,
        };
        let connector = HttpConnector::new(1);

        assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
    }

    #[test]
    fn test_errors_enforce_http() {
        let uri = "https://example.domain/foo/bar?baz".parse().unwrap();
        let dst = Destination {
            uri,
        };
        let connector = HttpConnector::new(1);

        assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
    }

    #[test]
    fn test_errors_missing_scheme() {
        let uri = "example.domain".parse().unwrap();
        let dst = Destination {
            uri,
        };
        let connector = HttpConnector::new(1);

        assert_eq!(connector.connect(dst).wait().unwrap_err().kind(), io::ErrorKind::InvalidInput);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use lazy_static::lazy_static;
use maplit::hashmap;
use specs::{Entities, Read, ReadStorage, System, WriteStorage};

use crate::animation::ImageType;
use crate::combat::components::collision::{CollisionBox, Points};
use crate::combat::components::intent::{AttackType, DefendType};
use crate::combat::components::state::HitType;
use crate::combat::components::{
    Action, Body, Collided, Draw, Facing, Health, Position, State, Weapon,
};
use crate::files::collide::CollisionBoxes;
use crate::game::EncounterTextures;
use crate::objects::TextureAtlas;
use crate::rect::{Point, Rect};

pub struct UpdateBoundingBoxes;

impl<'a> System<'a> for UpdateBoundingBoxes {
    type SystemData = (
        Read<'a, CollisionBoxes>,
        Read<'a, EncounterTextures>,
        ReadStorage<'a, Draw>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, State>,
        WriteStorage<'a, Body>,
        WriteStorage<'a, Weapon>,
    );

    fn run(
        &mut self,
        (
            collision_boxes,
            encounter_textures,
            draw,
            position,
            state,
            mut body,
            mut weapon,
        ): Self::SystemData,
    ) {
        use specs::Join;
        let collision_data = &collision_boxes.data;
        let textures = &encounter_textures.data;
        for (draw, position, state, body, weapon) in
            (&draw, &position, &state, &mut body, &mut weapon).join()
        {
            let mut weapon_boxes: Vec<Points> = vec![];
            let mut body_boxes: Vec<CollisionBox> = vec![];
            for image in draw.frame.images.iter() {
                match image.image_type {
                    ImageType::Collider => {
                        if let Some(Some(points)) = &collision_data
                            .get(&image.sheet)
                            .and_then(|v| v.points.get(image.image))
                        {
                            let direction = state.direction as i32;
                            let image_global_x: i32 = position.x as i32 + image.x * direction;
                            let image_global_y: i32 = position.y as i32 + image.y;
                            let rect_x = match state.direction {
                                Facing::Left => image_global_x - points.max_x as i32,
                                Facing::Right => image_global_x,
                            };
                            weapon_boxes.push(Points {
                                bounding: Rect {
                                    x: rect_x,
                                    y: image_global_y,
                                    w: points.max_x,
                                    h: points.max_y,
                                },
                                points: points
                                    .data
                                    .iter()
                                    .map(|p| Point {
                                        x: image_global_x + p.x * direction,
                                        y: image_global_y + p.y,
                                    })
                                    .collect(),
                            })
                        }
                    }
                    ImageType::Collidee => {
                        if let Some(texture) = textures.get(&image.sheet) {
                            let rect = &texture.rects[image.image];
                            let mut image_global_x: i32 =
                                position.x as i32 + image.x * state.direction as i32;
                            image_global_x = match state.direction {
                                Facing::Left => image_global_x - rect.w as i32,
                                Facing::Right => image_global_x,
                            };
                            body_boxes.push(CollisionBox {
                                rect: Rect {
                                    x: image_global_x,
                                    y: position.y as i32 + image.y,
                                    w: rect.w,
                                    h: rect.h,
                                },
                                sheet: image.sheet.clone(),
                                image_num: image.image as u32,
                            })
                        }
                    }
                    _ => (),
                }
            }

            if weapon_boxes.is_empty() {
                weapon.collision_points = None;
            } else {
                weapon.collision_points = Some(weapon_boxes);
            }

            if body_boxes.is_empty() {
                body.collision_boxes = None;
            } else {
                body.collision_boxes = Some(body_boxes);
            }
        }
    }
}

pub struct CheckCollisions;

impl<'a> System<'a> for CheckCollisions {
    type SystemData = (
        Read<'a, EncounterTextures>,
        ReadStorage<'a, State>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, Body>,
        ReadStorage<'a, Weapon>,
        WriteStorage<'a, Collided>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (encounter_textures, state, position, bodies, weapons, mut collided, entities): Self::SystemData,
    ) {
        use specs::Join;
        let textures = &encounter_textures.data;
        for (state, att_pos, weapon, attacker) in (&state, &position, &weapons, &*entities)
            .join()
            .filter(|(s, ..)| s.action.is_attack())
        {
            for (defender, body, defender_position) in (&*entities, &bodies, &position)
                .join()
                // ignore self for collision checking.
                .filter(|(defender, ..)| attacker.id() != defender.id())
                // ignore entities outside a 10 pixel y range.
                .filter(|(_, _, d_pos)| (att_pos.y as i32 - d_pos.y as i32).abs() < 10)
            {
                let hit = CheckCollisions::check_collision(&textures, weapon, body);
                if hit {
                    let result = collided.insert(attacker, Collided { target: defender });
                }
            }
        }
    }
}

impl CheckCollisions {
    fn check_collision(
        textures: &HashMap<String, TextureAtlas>,
        weapon: &Weapon,
        body: &Body,
    ) -> bool {
        for weapon_part in weapon.collision_points.iter().flat_map(|v| v) {
            // each attacking image in the attacker
            for body_part in body
                .collision_boxes
                .iter() // Option.iter()
                .flat_map(|v| v)
                .filter(|b| weapon_part.bounding.intersects(&b.rect))
            {
                // check that against each defending image in the defender
                for point in weapon_part
                    .points
                    .iter()
                    .filter(|p| body_part.rect.contains_point(p))
                {
                    // check the points collide and the pixel in the image is a part of the target
                    let hit_x = point.x - body_part.rect.x;
                    let hit_y = point.y - body_part.rect.y;
                    let texture = textures
                        .get(&body_part.sheet)
                        .expect("Encounter hasn't loaded correct textures to as world resource");
                    let collision_lookup = hit_x as usize * texture.image.width + hit_y as usize;
                    let pixel = texture.image.pixels[collision_lookup];
                    if pixel > 0 {
                        return true;
                    }
                }
            }
        }
        false
    }
}

lazy_static! {
    static ref blocks_attack: HashMap<Action, Action> = hashmap! {
        Action::Attack(AttackType::Swing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::BackSwing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::Thrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::Chop) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::UpThrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::ThrowDagger) => Action::Defend(DefendType::Dodge),
    };
}

pub struct ResolveCollisions;

impl<'a> System<'a> for ResolveCollisions {
    type SystemData = (
        WriteStorage<'a, Collided>,
        WriteStorage<'a, Health>,
        WriteStorage<'a, State>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (mut collided_storage, mut health_storage, mut state_storage, entities): Self::SystemData,
    ) {
        use specs::Join;
        for (collided, entity) in (collided_storage.drain(), &*entities).join() {
            let mut has_defended = false;
            let mut target_used_block = false;

            let target = &collided.target;
            {
                let state: Option<&State> = state_storage.get(entity);
                let target_state: Option<&State> = state_storage.get(*target);
                if let (Some(state), Some(target_state)) = (state, target_state) {
                    has_defended = blocks_attack
                        .get(&state.action)
                        .and_then(|a| Some(a == &target_state.action))
                        .expect(&format!(
                            "attack {:?} not in blocks_attack lookup",
                            &state.action
                        ));
                    target_used_block = target_state.action == Action::Defend(DefendType::Block)
                }
            }

            {
                if !has_defended {
                    // set defender animation
                    let target = &collided.target;
                    let target_health: Option<&mut Health> = health_storage.get_mut(*target);
                    let target_state: Option<&mut State> = state_storage.get_mut(*target);
                    if let (Some(target_health), Some(target_state)) = (target_health, target_state)
                    {
                        target_health.points -= 3; // TODO: change hard coded weapon damage
                        target_state.action = Action::Hit(HitType::Sliced);
                        target_state.ticks = 0;
                    }
                }
            }
            {
                // set attacker state
                let state: Option<&mut State> = state_storage.get_mut(entity);
                if let Some(state) = state {
                    if !has_defended || target_used_block {
                        state.action = Action::AttackRecovery;
                        state.ticks = 0;
                    }
                }
            }
        }
    }
}
<commit_msg>Do not use recovery animation for up thrust attack recovery.<commit_after>use std::collections::HashMap;

use lazy_static::lazy_static;
use maplit::hashmap;
use specs::{Entities, Read, ReadStorage, System, WriteStorage};

use crate::animation::ImageType;
use crate::combat::components::collision::{CollisionBox, Points};
use crate::combat::components::intent::{AttackType, DefendType};
use crate::combat::components::state::HitType;
use crate::combat::components::{
    Action, Body, Collided, Draw, Facing, Health, Position, State, Weapon,
};
use crate::files::collide::CollisionBoxes;
use crate::game::EncounterTextures;
use crate::objects::TextureAtlas;
use crate::rect::{Point, Rect};

pub struct UpdateBoundingBoxes;

impl<'a> System<'a> for UpdateBoundingBoxes {
    type SystemData = (
        Read<'a, CollisionBoxes>,
        Read<'a, EncounterTextures>,
        ReadStorage<'a, Draw>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, State>,
        WriteStorage<'a, Body>,
        WriteStorage<'a, Weapon>,
    );

    fn run(
        &mut self,
        (
            collision_boxes,
            encounter_textures,
            draw,
            position,
            state,
            mut body,
            mut weapon,
        ): Self::SystemData,
    ) {
        use specs::Join;
        let collision_data = &collision_boxes.data;
        let textures = &encounter_textures.data;
        for (draw, position, state, body, weapon) in
            (&draw, &position, &state, &mut body, &mut weapon).join()
        {
            let mut weapon_boxes: Vec<Points> = vec![];
            let mut body_boxes: Vec<CollisionBox> = vec![];
            for image in draw.frame.images.iter() {
                match image.image_type {
                    ImageType::Collider => {
                        if let Some(Some(points)) = &collision_data
                            .get(&image.sheet)
                            .and_then(|v| v.points.get(image.image))
                        {
                            let direction = state.direction as i32;
                            let image_global_x: i32 = position.x as i32 + image.x * direction;
                            let image_global_y: i32 = position.y as i32 + image.y;
                            let rect_x = match state.direction {
                                Facing::Left => image_global_x - points.max_x as i32,
                                Facing::Right => image_global_x,
                            };
                            weapon_boxes.push(Points {
                                bounding: Rect {
                                    x: rect_x,
                                    y: image_global_y,
                                    w: points.max_x,
                                    h: points.max_y,
                                },
                                points: points
                                    .data
                                    .iter()
                                    .map(|p| Point {
                                        x: image_global_x + p.x * direction,
                                        y: image_global_y + p.y,
                                    })
                                    .collect(),
                            })
                        }
                    }
                    ImageType::Collidee => {
                        if let Some(texture) = textures.get(&image.sheet) {
                            let rect = &texture.rects[image.image];
                            let mut image_global_x: i32 =
                                position.x as i32 + image.x * state.direction as i32;
                            image_global_x = match state.direction {
                                Facing::Left => image_global_x - rect.w as i32,
                                Facing::Right => image_global_x,
                            };
                            body_boxes.push(CollisionBox {
                                rect: Rect {
                                    x: image_global_x,
                                    y: position.y as i32 + image.y,
                                    w: rect.w,
                                    h: rect.h,
                                },
                                sheet: image.sheet.clone(),
                                image_num: image.image as u32,
                            })
                        }
                    }
                    _ => (),
                }
            }

            if weapon_boxes.is_empty() {
                weapon.collision_points = None;
            } else {
                weapon.collision_points = Some(weapon_boxes);
            }

            if body_boxes.is_empty() {
                body.collision_boxes = None;
            } else {
                body.collision_boxes = Some(body_boxes);
            }
        }
    }
}

pub struct CheckCollisions;

impl<'a> System<'a> for CheckCollisions {
    type SystemData = (
        Read<'a, EncounterTextures>,
        ReadStorage<'a, State>,
        ReadStorage<'a, Position>,
        ReadStorage<'a, Body>,
        ReadStorage<'a, Weapon>,
        WriteStorage<'a, Collided>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (encounter_textures, state, position, bodies, weapons, mut collided, entities): Self::SystemData,
    ) {
        use specs::Join;
        let textures = &encounter_textures.data;
        for (state, att_pos, weapon, attacker) in (&state, &position, &weapons, &*entities)
            .join()
            .filter(|(s, ..)| s.action.is_attack())
        {
            for (defender, body, defender_position) in (&*entities, &bodies, &position)
                .join()
                // ignore self for collision checking.
                .filter(|(defender, ..)| attacker.id() != defender.id())
                // ignore entities outside a 10 pixel y range.
                .filter(|(_, _, d_pos)| (att_pos.y as i32 - d_pos.y as i32).abs() < 10)
            {
                let hit = CheckCollisions::check_collision(&textures, weapon, body);
                if hit {
                    let result = collided.insert(attacker, Collided { target: defender });
                }
            }
        }
    }
}

impl CheckCollisions {
    fn check_collision(
        textures: &HashMap<String, TextureAtlas>,
        weapon: &Weapon,
        body: &Body,
    ) -> bool {
        for weapon_part in weapon.collision_points.iter().flat_map(|v| v) {
            // each attacking image in the attacker
            for body_part in body
                .collision_boxes
                .iter() // Option.iter()
                .flat_map(|v| v)
                .filter(|b| weapon_part.bounding.intersects(&b.rect))
            {
                // check that against each defending image in the defender
                for point in weapon_part
                    .points
                    .iter()
                    .filter(|p| body_part.rect.contains_point(p))
                {
                    // check the points collide and the pixel in the image is a part of the target
                    let hit_x = point.x - body_part.rect.x;
                    let hit_y = point.y - body_part.rect.y;
                    let texture = textures
                        .get(&body_part.sheet)
                        .expect("Encounter hasn't loaded correct textures to as world resource");
                    let collision_lookup = hit_x as usize * texture.image.width + hit_y as usize;
                    let pixel = texture.image.pixels[collision_lookup];
                    if pixel > 0 {
                        return true;
                    }
                }
            }
        }
        false
    }
}

lazy_static! {
    static ref blocks_attack: HashMap<Action, Action> = hashmap! {
        Action::Attack(AttackType::Swing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::BackSwing) => Action::Defend(DefendType::Block),
        Action::Attack(AttackType::Thrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::Chop) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::UpThrust) => Action::Defend(DefendType::Dodge),
        Action::Attack(AttackType::ThrowDagger) => Action::Defend(DefendType::Dodge),
    };
}

pub struct ResolveCollisions;

impl<'a> System<'a> for ResolveCollisions {
    type SystemData = (
        WriteStorage<'a, Collided>,
        WriteStorage<'a, Health>,
        WriteStorage<'a, State>,
        Entities<'a>,
    );

    fn run(
        &mut self,
        (mut collided_storage, mut health_storage, mut state_storage, entities): Self::SystemData,
    ) {
        use specs::Join;
        for (collided, entity) in (collided_storage.drain(), &*entities).join() {
            let mut has_defended = false;
            let mut target_used_block = false;

            let target = &collided.target;
            {
                let state: Option<&State> = state_storage.get(entity);
                let target_state: Option<&State> = state_storage.get(*target);
                if let (Some(state), Some(target_state)) = (state, target_state) {
                    has_defended = blocks_attack
                        .get(&state.action)
                        .and_then(|a| Some(a == &target_state.action))
                        .expect(&format!(
                            "attack {:?} not in blocks_attack lookup",
                            &state.action
                        ));
                    target_used_block = target_state.action == Action::Defend(DefendType::Block)
                }
            }

            {
                if !has_defended {
                    // set defender animation
                    let target = &collided.target;
                    let target_health: Option<&mut Health> = health_storage.get_mut(*target);
                    let target_state: Option<&mut State> = state_storage.get_mut(*target);
                    if let (Some(target_health), Some(target_state)) = (target_health, target_state)
                    {
                        target_health.points -= 3; // TODO: change hard coded weapon damage
                        target_state.action = Action::Hit(HitType::Sliced);
                        target_state.ticks = 0;
                    }
                }
            }
            {
                // set attacker state
                let state: Option<&mut State> = state_storage.get_mut(entity);
                if let Some(state) = state {
                    if !has_defended || target_used_block {
                        match state.action {
                            Action::Attack(AttackType::UpThrust) => (),
                            _ =>  {
                                state.action = Action::AttackRecovery;
                                state.ticks = 0;
                            }
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::f32::consts::{FRAC_1_PI, PI};
use std::path::Path;
use std::cmp::min;
use std::sync::Arc;

use num::Zero;
use parking_lot::RwLock;

use {Point2f, Point2i, Point3f, Transform, Vector3f};
use geometry::{spherical_phi, spherical_theta};
use imageio::read_image;
use interaction::Interaction;
use light::{Light, LightFlags, VisibilityTester};
use mipmap::{MIPMap, WrapMode};
use paramset::ParamSet;
use ray::Ray;
use sampling::Distribution2D;
use scene::Scene;
use spectrum::Spectrum;

#[derive(Debug)]
pub struct InfiniteAreaLight {
    id: u32,
    light_to_world: Transform,
    world_to_light: Transform,
    n_samples: u32,
    l_map: Box<MIPMap<Spectrum>>,
    world_center: RwLock<Point3f>,
    world_radius: RwLock<f32>,
    distribution: Box<Distribution2D>,
}

impl InfiniteAreaLight {
    pub fn new<P: AsRef<Path>>(
        l2w: Transform,
        n_samples: u32,
        power: Spectrum,
        texmap: P,
    ) -> InfiniteAreaLight {
        let texmap = texmap.as_ref();
        // Read texel data from texmap and initialise Lmap
        let (resolution, texels) = if let Ok((pixels, res)) = read_image(texmap) {
            info!(
                "Loading environment map {} for infinite light",
                texmap.display()
            );
            let texels = pixels.iter().map(|s| *s * power).collect();
            (res, texels)
        } else {
            warn!(
                "Environment map {} for infinite light not found! Using constant texture \
                 instead.",
                texmap.display()
            );
            (Point2i::new(1, 1), vec![power])
        };
        //
        let l_map = Box::new(MIPMap::new(
            &resolution,
            &texels[..],
            false,
            0.0,
            WrapMode::Repeat,
        ));
        // initialize sampling PDFs for infinite area light
        // - compute scalar-valued image img from environment map
        let (width, height) = (2 * l_map.width(), 2 * l_map.height());
        let filter = 0.5 / min(width, height) as f32;
        let mut img = Vec::with_capacity(width * height);
        for v in 0..height {
            let vp = (v as f32 + 0.5) / height as f32;
            let sin_theta = (PI * (v as f32 + 0.5) / height as f32).sin();
            for u in 0..width {
                let up = (u as f32 + 0.5) / width as f32;
                img.push(l_map.lookup(&Point2f::new(up, vp), filter).y() * sin_theta);
            }
        }
        // - compute sampling distributions for rows and columns of image
        let distribution = Box::new(Distribution2D::new(&img[..], width, height));

        InfiniteAreaLight {
            id: super::get_next_id(),
            world_to_light: l2w.inverse(),
            light_to_world: l2w,
            n_samples: n_samples,
            l_map: l_map,
            world_center: RwLock::new(Point3f::zero()),
            world_radius: RwLock::new(0.0),
            distribution: distribution,
        }
    }

    pub fn create(l2w: &Transform, params: &ParamSet) -> Arc<Light> {
        let L = params.find_one_spectrum("L", Spectrum::white());
        let scale = params.find_one_spectrum("scale", Spectrum::white());
        let mapname = params.find_one_filename("mapname", "".to_owned());
        let n_samples = params.find_one_int("samples", 1);
        // TODO quickrender
        Arc::new(InfiniteAreaLight::new(
            l2w.clone(),
            n_samples as u32,
            L * scale,
            mapname,
        ))
    }
}

impl Light for InfiniteAreaLight {
    fn id(&self) -> u32 {
        self.id
    }

    fn preprocess(&self, scene: &Scene) {
        let (w_center, w_radius) = scene.world_bounds().bounding_sphere();
        let mut wc = self.world_center.write();
        *wc = w_center;
        let mut wr = self.world_radius.write();
        *wr = w_radius;
    }

    fn sample_li(
        &self,
        isect: &Interaction,
        u: &Point2f,
    ) -> (Spectrum, Vector3f, f32, VisibilityTester) {
        // Find (u, v) sample coordinates in infinite light texture
        let (uv, map_pdf) = self.distribution.sample_continuous(u);
        if map_pdf == 0.0 {
            return (
                Spectrum::black(),
                Vector3f::new(0.0, 0.0, 0.0),
                0.0,
                VisibilityTester::new(
                    Interaction::from_point(&Point3f::zero()),
                    Interaction::from_point(&Point3f::zero()),
                ),
            );
        }
        // Convert infinite light sample point to direction
        let theta = uv[1] * PI;
        let phi = uv[0] * 2.0 * PI;
        let cos_theta = theta.cos();
        let sin_theta = theta.sin();
        let cos_phi = phi.cos();
        let sin_phi = phi.sin();
        let wi = &self.light_to_world
            * &Vector3f::new(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta);
        // Compute PDF for sampled infinite light direction
        let pdf = if sin_theta == 0.0 {
            0.0
        } else {
            map_pdf / (2.0 * PI * PI * sin_theta)
        };
        // Return radiance value for infinite light direction
        let world_radius = self.world_radius.read();
        let target = isect.p + wi * (2.0 * *world_radius);
        let vis = VisibilityTester::new(*isect, Interaction::from_point(&target));
        (self.l_map.lookup(&uv, 0.0), wi, pdf, vis)
    }

    fn pdf_li(&self, _si: &Interaction, w: &Vector3f) -> f32 {
        let wi = &self.world_to_light * w;
        let theta = spherical_theta(&wi);
        let phi = spherical_phi(&wi);
        let sin_theta = theta.sin();

        if sin_theta == 0.0 {
            0.0
        } else {
            self.distribution
                .pdf(&Point2f::new(phi * FRAC_1_PI * 0.5, theta * FRAC_1_PI))
                / (2.0 * PI * PI * sin_theta)
        }
    }

    fn n_samples(&self) -> u32 {
        self.n_samples
    }

    fn flags(&self) -> LightFlags {
        LightFlags::INFINITE
    }

    fn power(&self) -> Spectrum {
        let world_radius = self.world_radius.read();
        PI * *world_radius * *world_radius * self.l_map.lookup(&Point2f::new(0.5, 0.5), 0.5)
    }

    fn le(&self, ray: &Ray) -> Spectrum {
        let w = (&self.world_to_light * &ray.d).normalize();
        let st = Point2f::new(
            spherical_phi(&w) * FRAC_1_PI * 0.5,
            spherical_theta(&w) * FRAC_1_PI,
        );

        self.l_map.lookup(&st, 0.0)
    }
}
<commit_msg>Parallelize generation of sampling pdfs for inifinite light<commit_after>use std::f32::consts::{FRAC_1_PI, PI};
use std::path::Path;
use std::cmp::min;
use std::sync::Arc;

use ndarray::prelude::*;
use ndarray_parallel::prelude::*;
use num::Zero;
use parking_lot::RwLock;

use {Point2f, Point2i, Point3f, Transform, Vector3f};
use geometry::{spherical_phi, spherical_theta};
use imageio::read_image;
use interaction::Interaction;
use light::{Light, LightFlags, VisibilityTester};
use mipmap::{MIPMap, WrapMode};
use paramset::ParamSet;
use ray::Ray;
use sampling::Distribution2D;
use scene::Scene;
use spectrum::Spectrum;

#[derive(Debug)]
pub struct InfiniteAreaLight {
    id: u32,
    light_to_world: Transform,
    world_to_light: Transform,
    n_samples: u32,
    l_map: Box<MIPMap<Spectrum>>,
    world_center: RwLock<Point3f>,
    world_radius: RwLock<f32>,
    distribution: Box<Distribution2D>,
}

impl InfiniteAreaLight {
    pub fn new<P: AsRef<Path>>(
        l2w: Transform,
        n_samples: u32,
        power: Spectrum,
        texmap: P,
    ) -> InfiniteAreaLight {
        let texmap = texmap.as_ref();
        // Read texel data from texmap and initialise Lmap
        let (resolution, texels) = if let Ok((pixels, res)) = read_image(texmap) {
            info!(
                "Loading environment map {} for infinite light",
                texmap.display()
            );
            let texels = pixels.iter().map(|s| *s * power).collect();
            (res, texels)
        } else {
            warn!(
                "Environment map {} for infinite light not found! Using constant texture \
                 instead.",
                texmap.display()
            );
            (Point2i::new(1, 1), vec![power])
        };
        //
        let l_map = Box::new(MIPMap::new(
            &resolution,
            &texels[..],
            false,
            0.0,
            WrapMode::Repeat,
        ));
        // initialize sampling PDFs for infinite area light
        // - compute scalar-valued image img from environment map
        let (width, height) = (2 * l_map.width(), 2 * l_map.height());
        let filter = 0.5 / min(width, height) as f32;
        let mut img = Array2::zeros((width, height));

        img.axis_iter_mut(Axis(1))
            .into_par_iter()
            .enumerate()
            .for_each(|(v, mut row)| {
                let vp = (v as f32 + 0.5) / height as f32;
                let sin_theta = (PI * (v as f32 + 0.5) / height as f32).sin();
                for u in 0..width {
                    let up = (u as f32 + 0.5) / width as f32;
                    row[u] = l_map.lookup(&Point2f::new(up, vp), filter).y() * sin_theta;
                }
            });

        // - compute sampling distributions for rows and columns of image
        let distribution = Box::new(Distribution2D::new(
            img.view().into_slice().unwrap(),
            width,
            height,
        ));

        InfiniteAreaLight {
            id: super::get_next_id(),
            world_to_light: l2w.inverse(),
            light_to_world: l2w,
            n_samples: n_samples,
            l_map: l_map,
            world_center: RwLock::new(Point3f::zero()),
            world_radius: RwLock::new(0.0),
            distribution: distribution,
        }
    }

    pub fn create(l2w: &Transform, params: &ParamSet) -> Arc<Light> {
        let L = params.find_one_spectrum("L", Spectrum::white());
        let scale = params.find_one_spectrum("scale", Spectrum::white());
        let mapname = params.find_one_filename("mapname", "".to_owned());
        let n_samples = params.find_one_int("samples", 1);
        // TODO quickrender
        Arc::new(InfiniteAreaLight::new(
            l2w.clone(),
            n_samples as u32,
            L * scale,
            mapname,
        ))
    }
}

impl Light for InfiniteAreaLight {
    fn id(&self) -> u32 {
        self.id
    }

    fn preprocess(&self, scene: &Scene) {
        let (w_center, w_radius) = scene.world_bounds().bounding_sphere();
        let mut wc = self.world_center.write();
        *wc = w_center;
        let mut wr = self.world_radius.write();
        *wr = w_radius;
    }

    fn sample_li(
        &self,
        isect: &Interaction,
        u: &Point2f,
    ) -> (Spectrum, Vector3f, f32, VisibilityTester) {
        // Find (u, v) sample coordinates in infinite light texture
        let (uv, map_pdf) = self.distribution.sample_continuous(u);
        if map_pdf == 0.0 {
            return (
                Spectrum::black(),
                Vector3f::new(0.0, 0.0, 0.0),
                0.0,
                VisibilityTester::new(
                    Interaction::from_point(&Point3f::zero()),
                    Interaction::from_point(&Point3f::zero()),
                ),
            );
        }
        // Convert infinite light sample point to direction
        let theta = uv[1] * PI;
        let phi = uv[0] * 2.0 * PI;
        let cos_theta = theta.cos();
        let sin_theta = theta.sin();
        let cos_phi = phi.cos();
        let sin_phi = phi.sin();
        let wi = &self.light_to_world
            * &Vector3f::new(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta);
        // Compute PDF for sampled infinite light direction
        let pdf = if sin_theta == 0.0 {
            0.0
        } else {
            map_pdf / (2.0 * PI * PI * sin_theta)
        };
        // Return radiance value for infinite light direction
        let world_radius = self.world_radius.read();
        let target = isect.p + wi * (2.0 * *world_radius);
        let vis = VisibilityTester::new(*isect, Interaction::from_point(&target));
        (self.l_map.lookup(&uv, 0.0), wi, pdf, vis)
    }

    fn pdf_li(&self, _si: &Interaction, w: &Vector3f) -> f32 {
        let wi = &self.world_to_light * w;
        let theta = spherical_theta(&wi);
        let phi = spherical_phi(&wi);
        let sin_theta = theta.sin();

        if sin_theta == 0.0 {
            0.0
        } else {
            self.distribution
                .pdf(&Point2f::new(phi * FRAC_1_PI * 0.5, theta * FRAC_1_PI))
                / (2.0 * PI * PI * sin_theta)
        }
    }

    fn n_samples(&self) -> u32 {
        self.n_samples
    }

    fn flags(&self) -> LightFlags {
        LightFlags::INFINITE
    }

    fn power(&self) -> Spectrum {
        let world_radius = self.world_radius.read();
        PI * *world_radius * *world_radius * self.l_map.lookup(&Point2f::new(0.5, 0.5), 0.5)
    }

    fn le(&self, ray: &Ray) -> Spectrum {
        let w = (&self.world_to_light * &ray.d).normalize();
        let st = Point2f::new(
            spherical_phi(&w) * FRAC_1_PI * 0.5,
            spherical_theta(&w) * FRAC_1_PI,
        );

        self.l_map.lookup(&st, 0.0)
    }
}
<|endoftext|>


<commit_before>use diesel;
use diesel::pg::PgConnection;
use models::*;
use diesel::prelude::*;

pub fn get_notes(conn: &PgConnection) -> Vec<Note> {
    use schema::notes::dsl::*;

    notes.filter(pinned.eq(false))
        .limit(5)
        .load::<Note>(conn)
        .expect("Error loading notes")
}

pub fn create_note(conn: &PgConnection, note: NewNote) -> Note {
    use schema::notes;

    diesel::insert(&note)
        .into(notes::table)
        .get_result(conn)
        .expect("Error saving new note")
}
<commit_msg>Simplified get_notes function<commit_after>use diesel;
use diesel::pg::PgConnection;
use models::*;
use diesel::prelude::*;

pub fn get_notes(conn: &PgConnection) -> Vec<Note> {
    use schema::notes::dsl::*;

    notes.load::<Note>(conn).expect("Error loading notes")
}

pub fn create_note(conn: &PgConnection, note: NewNote) -> Note {
    use schema::notes;

    diesel::insert(&note)
        .into(notes::table)
        .get_result(conn)
        .expect("Error saving new note")
}
<|endoftext|>