<commit_before>// Copyright 2016 metal-rs developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use cocoa::foundation::{NSUInteger};
use objc::runtime::{Object, Class, YES, NO};
use objc_foundation::{NSString, INSString};

use super::{id, nil, NSObjectPrototype, NSObjectProtocol};

use resource::MTLResourceOptions;
use commandqueue::MTLCommandQueue;
use pipeline::{MTLRenderPipelineState, MTLRenderPipelineDescriptor,
               MTLRenderPipelineReflection};
use library::{MTLLibrary, MTLCompileOptions};
use types::{MTLSize};
use buffer::MTLBuffer;
use texture::{MTLTexture, MTLTextureDescriptor};
use sampler::{MTLSamplerState, MTLSamplerDescriptor};
use depthstencil::{MTLDepthStencilDescriptor, MTLDepthStencilState};

use libc;

use std::marker::PhantomData;
use std::ffi::CStr;

#[allow(non_camel_case_types)]
#[repr(u64)]
#[derive(Copy, Clone)]
pub enum MTLFeatureSet {
    iOS_GPUFamily1_v1 = 0,
    iOS_GPUFamily2_v1 = 1,
    iOS_GPUFamily1_v2 = 2,
    iOS_GPUFamily2_v2 = 3,
    iOS_GPUFamily3_v1 = 4,
    OSX_GPUFamily1_v1 = 10000,
}

bitflags! {
    flags MTLPipelineOption: NSUInteger {
        const MTLPipelineOptionNone           = 0,
        const MTLPipelineOptionArgumentInfo   = 1 << 0,
        const MTLPipelineOptionBufferTypeInfo = 1 << 1
    }
}

#[link(name = "Metal", kind = "framework")]
extern {
    fn MTLCreateSystemDefaultDevice() -> *mut Object;
}

pub fn create_system_default_device() -> MTLDevice {
    unsafe {
        id(MTLCreateSystemDefaultDevice(), PhantomData)
    }
}

/*type MTLNewLibraryCompletionHandler = extern fn(library: id, error: id);
type MTLNewRenderPipelineStateCompletionHandler = extern fn(renderPipelineState: id, error: id);
type MTLNewRenderPipelineStateWithReflectionCompletionHandler = extern fn(renderPipelineState: id, reflection: id, error: id);
type MTLNewComputePipelineStateCompletionHandler = extern fn(computePipelineState: id, error: id);
type MTLNewComputePipelineStateWithReflectionCompletionHandler = extern fn(computePipelineState: id, reflection: id, error: id);*/

pub enum MTLDevicePrototype {}
pub type MTLDevice = id<(MTLDevicePrototype, (NSObjectPrototype, ()))>;

impl<'a> MTLDevice {
    pub fn name(&'a self) -> &'a str {
        unsafe {
            let name: &'a NSString = msg_send![self.0, name];
            name.as_str()
        }
    }

    pub fn vendor(&'a self) -> &'a str {
        unsafe {
            let name: &'a NSString = msg_send![self.0, vendorName];
            name.as_str()
        }
    }

    pub fn family_name(&'a self) -> &'a str {
        unsafe {
            let name: &'a NSString = msg_send![self.0, familyName];
            name.as_str()
        }
    }

    pub fn max_threads_per_threadgroup(&self) -> MTLSize {
        unsafe {
            msg_send![self.0, maxThreadsPerThreadgroup]
        }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe {
            match msg_send![self.0, isLowPower] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe {
            match msg_send![self.0, isHeadless] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_feature_set(&self, feature: MTLFeatureSet) -> bool {
        unsafe {
            match msg_send![self.0, supportsFeatureSet:feature] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_sample_count(&self, count: NSUInteger) -> bool {
        unsafe {
            match msg_send![self.0, supportsTextureSampleCount:count] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn d24_s8_supported(&self) -> bool {
        unsafe {
            match msg_send![self.0, isDepth24Stencil8PixelFormatSupported] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn new_command_queue(&self) -> MTLCommandQueue {
        unsafe {
            msg_send![self.0, newCommandQueue]
        }
    }

    pub fn new_default_library(&self) -> MTLLibrary {
        unsafe {
            msg_send![self.0, newDefaultLibrary]
        }
    }

    pub fn new_library_with_source(&self, src: &str, options: MTLCompileOptions) -> Result<MTLLibrary, String> {
        use cocoa::foundation::NSString as cocoa_NSString;
        use cocoa::base::nil as cocoa_nil;

        unsafe {
            let source = cocoa_NSString::alloc(cocoa_nil).init_str(src);
            let mut err = nil;

            let library: MTLLibrary = msg_send![self.0, newLibraryWithSource:source
                                                                     options:options
                                                                       error:&mut err];

            match library.is_null() {
                false => Ok(library),
                true => {
                    let desc: id = msg_send![err.0, localizedDescription];
                    let compile_error: *const libc::c_char = msg_send![desc.0, UTF8String];
                    Err(CStr::from_ptr(compile_error).to_string_lossy().into_owned())
                }
            }
        }
    }

    pub fn new_render_pipeline_state_with_reflection(&self, descriptor: MTLRenderPipelineDescriptor, reflection: *mut MTLRenderPipelineReflection) -> Result<MTLRenderPipelineState, String> {
        unsafe {
            let reflection_options = MTLPipelineOptionArgumentInfo | MTLPipelineOptionBufferTypeInfo;
            let mut err = nil;

            let pipeline_state: MTLRenderPipelineState = msg_send![self.0, newRenderPipelineStateWithDescriptor:descriptor.0
                                                                                options:reflection_options
                                                                                reflection:reflection
                                                                                error:&mut err];

            match pipeline_state.is_null() {
                false => Ok(pipeline_state),
                true => {
                    let desc: id = msg_send![err.0, localizedDescription];
                    let compile_error: *const libc::c_char = msg_send![desc.0, UTF8String];
                    Err(CStr::from_ptr(compile_error).to_string_lossy().into_owned())
                }
            }
        }

    }

    pub fn new_render_pipeline_state(&self, descriptor: MTLRenderPipelineDescriptor) -> Result<MTLRenderPipelineState, ()> {
        unsafe {
            let pipeline_state: MTLRenderPipelineState = msg_send![self.0, newRenderPipelineStateWithDescriptor:descriptor.0
                                                                                error:nil];

            match pipeline_state.is_null() {
                true => Err(()),
                false => Ok(pipeline_state)
            }
        }
    }

    pub fn new_buffer(&self, length: u64, options: MTLResourceOptions) -> MTLBuffer {
        unsafe {
            msg_send![self.0, newBufferWithLength:length
                                          options:options]
        }
    }

    pub fn new_buffer_with_data(&self, bytes: *const libc::c_void, length: NSUInteger, options: MTLResourceOptions) -> MTLBuffer {
        unsafe {
            msg_send![self.0, newBufferWithBytes:bytes
                                          length:length
                                         options:options]
        }
    }

    pub fn new_texture(&self, descriptor: MTLTextureDescriptor) -> MTLTexture {
        unsafe {
            msg_send![self.0, newTextureWithDescriptor:descriptor.0]
        }
    }

    pub fn new_sampler(&self, descriptor: MTLSamplerDescriptor) -> MTLSamplerState {
        unsafe {
            msg_send![self.0, newSamplerStateWithDescriptor:descriptor.0]
        }
    }

    pub fn new_depth_stencil_state(&self, descriptor: MTLDepthStencilDescriptor) -> MTLDepthStencilState {
        unsafe {
            msg_send![self.0, newDepthStencilStateWithDescriptor:descriptor]
        }
    }
}

impl NSObjectProtocol for MTLDevice {
    unsafe fn class() -> &'static Class {
        Class::get("MTLDevice").unwrap()
    }
}

<commit_msg>Added new_library_with_file to MTLDevice<commit_after>// Copyright 2016 metal-rs developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

use cocoa::foundation::{NSUInteger};
use objc::runtime::{Object, Class, YES, NO};
use objc_foundation::{NSString, INSString};

use super::{id, nil, NSObjectPrototype, NSObjectProtocol};

use resource::MTLResourceOptions;
use commandqueue::MTLCommandQueue;
use pipeline::{MTLRenderPipelineState, MTLRenderPipelineDescriptor,
               MTLRenderPipelineReflection};
use library::{MTLLibrary, MTLCompileOptions};
use types::{MTLSize};
use buffer::MTLBuffer;
use texture::{MTLTexture, MTLTextureDescriptor};
use sampler::{MTLSamplerState, MTLSamplerDescriptor};
use depthstencil::{MTLDepthStencilDescriptor, MTLDepthStencilState};

use libc;

use std::marker::PhantomData;
use std::ffi::CStr;
use std::path::Path;

#[allow(non_camel_case_types)]
#[repr(u64)]
#[derive(Copy, Clone)]
pub enum MTLFeatureSet {
    iOS_GPUFamily1_v1 = 0,
    iOS_GPUFamily2_v1 = 1,
    iOS_GPUFamily1_v2 = 2,
    iOS_GPUFamily2_v2 = 3,
    iOS_GPUFamily3_v1 = 4,
    OSX_GPUFamily1_v1 = 10000,
}

bitflags! {
    flags MTLPipelineOption: NSUInteger {
        const MTLPipelineOptionNone           = 0,
        const MTLPipelineOptionArgumentInfo   = 1 << 0,
        const MTLPipelineOptionBufferTypeInfo = 1 << 1
    }
}

#[link(name = "Metal", kind = "framework")]
extern {
    fn MTLCreateSystemDefaultDevice() -> *mut Object;
}

pub fn create_system_default_device() -> MTLDevice {
    unsafe {
        id(MTLCreateSystemDefaultDevice(), PhantomData)
    }
}

/*type MTLNewLibraryCompletionHandler = extern fn(library: id, error: id);
type MTLNewRenderPipelineStateCompletionHandler = extern fn(renderPipelineState: id, error: id);
type MTLNewRenderPipelineStateWithReflectionCompletionHandler = extern fn(renderPipelineState: id, reflection: id, error: id);
type MTLNewComputePipelineStateCompletionHandler = extern fn(computePipelineState: id, error: id);
type MTLNewComputePipelineStateWithReflectionCompletionHandler = extern fn(computePipelineState: id, reflection: id, error: id);*/

pub enum MTLDevicePrototype {}
pub type MTLDevice = id<(MTLDevicePrototype, (NSObjectPrototype, ()))>;

impl<'a> MTLDevice {
    pub fn name(&'a self) -> &'a str {
        unsafe {
            let name: &'a NSString = msg_send![self.0, name];
            name.as_str()
        }
    }

    pub fn vendor(&'a self) -> &'a str {
        unsafe {
            let name: &'a NSString = msg_send![self.0, vendorName];
            name.as_str()
        }
    }

    pub fn family_name(&'a self) -> &'a str {
        unsafe {
            let name: &'a NSString = msg_send![self.0, familyName];
            name.as_str()
        }
    }

    pub fn max_threads_per_threadgroup(&self) -> MTLSize {
        unsafe {
            msg_send![self.0, maxThreadsPerThreadgroup]
        }
    }

    pub fn is_low_power(&self) -> bool {
        unsafe {
            match msg_send![self.0, isLowPower] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn is_headless(&self) -> bool {
        unsafe {
            match msg_send![self.0, isHeadless] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_feature_set(&self, feature: MTLFeatureSet) -> bool {
        unsafe {
            match msg_send![self.0, supportsFeatureSet:feature] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn supports_sample_count(&self, count: NSUInteger) -> bool {
        unsafe {
            match msg_send![self.0, supportsTextureSampleCount:count] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn d24_s8_supported(&self) -> bool {
        unsafe {
            match msg_send![self.0, isDepth24Stencil8PixelFormatSupported] {
                YES => true,
                NO => false,
                _ => unreachable!()
            }
        }
    }

    pub fn new_command_queue(&self) -> MTLCommandQueue {
        unsafe {
            msg_send![self.0, newCommandQueue]
        }
    }

    pub fn new_default_library(&self) -> MTLLibrary {
        unsafe {
            msg_send![self.0, newDefaultLibrary]
        }
    }

    pub fn new_library_with_source(&self, src: &str, options: MTLCompileOptions) -> Result<MTLLibrary, String> {
        use cocoa::foundation::NSString as cocoa_NSString;
        use cocoa::base::nil as cocoa_nil;

        unsafe {
            let source = cocoa_NSString::alloc(cocoa_nil).init_str(src);
            let mut err = nil;

            let library: MTLLibrary = msg_send![self.0, newLibraryWithSource:source
                                                                     options:options
                                                                       error:&mut err];

            match library.is_null() {
                false => Ok(library),
                true => {
                    let desc: id = msg_send![err.0, localizedDescription];
                    let compile_error: *const libc::c_char = msg_send![desc.0, UTF8String];
                    Err(CStr::from_ptr(compile_error).to_string_lossy().into_owned())
                }
            }
        }
    }

    pub fn new_library_with_file<P: AsRef<Path>>(&self, file: P) -> Result<MTLLibrary, String> {
        use cocoa::foundation::NSString as cocoa_NSString;
        use cocoa::base::nil as cocoa_nil;

        unsafe {
            let filename = cocoa_NSString::alloc(cocoa_nil)
                .init_str(file.as_ref().to_string_lossy().as_ref());
            let mut err = nil;

            let library: MTLLibrary = msg_send![self.0, newLibraryWithFile:filename
                                                                     error:&mut err];

            match library.is_null() {
                false => Ok(library),
                true => {
                    let desc: id = msg_send![err.0, localizedDescription];
                    let compile_error: *const libc::c_char = msg_send![desc.0, UTF8String];
                    Err(CStr::from_ptr(compile_error).to_string_lossy().into_owned())
                }
            }
        }
    }

    pub fn new_render_pipeline_state_with_reflection(&self, descriptor: MTLRenderPipelineDescriptor, reflection: *mut MTLRenderPipelineReflection) -> Result<MTLRenderPipelineState, String> {
        unsafe {
            let reflection_options = MTLPipelineOptionArgumentInfo | MTLPipelineOptionBufferTypeInfo;
            let mut err = nil;

            let pipeline_state: MTLRenderPipelineState = msg_send![self.0, newRenderPipelineStateWithDescriptor:descriptor.0
                                                                                options:reflection_options
                                                                                reflection:reflection
                                                                                error:&mut err];

            match pipeline_state.is_null() {
                false => Ok(pipeline_state),
                true => {
                    let desc: id = msg_send![err.0, localizedDescription];
                    let compile_error: *const libc::c_char = msg_send![desc.0, UTF8String];
                    Err(CStr::from_ptr(compile_error).to_string_lossy().into_owned())
                }
            }
        }

    }

    pub fn new_render_pipeline_state(&self, descriptor: MTLRenderPipelineDescriptor) -> Result<MTLRenderPipelineState, ()> {
        unsafe {
            let pipeline_state: MTLRenderPipelineState = msg_send![self.0, newRenderPipelineStateWithDescriptor:descriptor.0
                                                                                error:nil];

            match pipeline_state.is_null() {
                true => Err(()),
                false => Ok(pipeline_state)
            }
        }
    }

    pub fn new_buffer(&self, length: u64, options: MTLResourceOptions) -> MTLBuffer {
        unsafe {
            msg_send![self.0, newBufferWithLength:length
                                          options:options]
        }
    }

    pub fn new_buffer_with_data(&self, bytes: *const libc::c_void, length: NSUInteger, options: MTLResourceOptions) -> MTLBuffer {
        unsafe {
            msg_send![self.0, newBufferWithBytes:bytes
                                          length:length
                                         options:options]
        }
    }

    pub fn new_texture(&self, descriptor: MTLTextureDescriptor) -> MTLTexture {
        unsafe {
            msg_send![self.0, newTextureWithDescriptor:descriptor.0]
        }
    }

    pub fn new_sampler(&self, descriptor: MTLSamplerDescriptor) -> MTLSamplerState {
        unsafe {
            msg_send![self.0, newSamplerStateWithDescriptor:descriptor.0]
        }
    }

    pub fn new_depth_stencil_state(&self, descriptor: MTLDepthStencilDescriptor) -> MTLDepthStencilState {
        unsafe {
            msg_send![self.0, newDepthStencilStateWithDescriptor:descriptor]
        }
    }
}

impl NSObjectProtocol for MTLDevice {
    unsafe fn class() -> &'static Class {
        Class::get("MTLDevice").unwrap()
    }
}

<|endoftext|>


<commit_before>use libc::{c_int, c_uint, c_ulonglong};
use ffi::{core, target};
use ffi::execution_engine as engine;
use ffi::execution_engine::*;
use ffi::target_machine::LLVMCodeModel;
use cbox::{CBox, CSemiBox, DisposeRef};
use std::marker::PhantomData;
use std::{mem, ptr};
use std::ops::*;
use compile::Compile;
use context::{Context, GetContext};
use module::Module;
use ty::{StructType, Type};
use util::{self, CastFrom};
use value::{Function, Value};

/// An abstract interface for implementation execution of LLVM modules.
///
/// This is designed to support both interpreter and just-in-time (JIT) compiler implementations.
pub trait ExecutionEngine<'a>:'a + Sized + DisposeRef where LLVMExecutionEngineRef: From<&'a Self> {
    /// The options given to the engine upon creation.
    type Options : Copy;
    /// Create a new execution engine with the given `Module` and optiions, or return a
    /// description of the error.
    fn new(module: &'a Module, options: Self::Options) -> Result<CSemiBox<'a, Self>, CBox<str>>;

    /// Add a module to the list of modules to interpret or compile.
    fn add_module(&'a self, module: &'a Module) {
        unsafe { engine::LLVMAddModule(self.into(), (&*module).into()) }
    }
    /// Remove a module from the list of modules to interpret or compile.
    fn remove_module(&'a self, module: &'a Module) -> &'a Module {
        unsafe {
            let mut out = mem::uninitialized();
            engine::LLVMRemoveModule(self.into(), module.into(), &mut out, ptr::null_mut());
            out.into()
        }
    }
    /// Execute all of the static constructors for this program.
    fn run_static_constructors(&'a self) {
        unsafe { engine::LLVMRunStaticConstructors(self.into()) }
    }
    /// Execute all of the static destructors for this program.
    fn run_static_destructors(&'a self) {
        unsafe { engine::LLVMRunStaticDestructors(self.into()) }
    }
    /// Attempt to find a function with the name given, or `None` if there wasn't
    /// a function with that name.
    fn find_function(&'a self, name: &str) -> Option<&'a Function> {
        util::with_cstr(name, |c_name| unsafe {
            let mut out = mem::zeroed();
            engine::LLVMFindFunction(self.into(), c_name, &mut out);
            mem::transmute(out)
        })
    }
    /// Run `function` with the arguments given as ``GenericValue`s, then return the result as one.
    ///
    /// Note that if this engine is a `JitEngine`, it only supports a small fraction of combinations
    /// for the arguments and return value, so be warned.
    ///
    /// To convert the arguments to `GenericValue`s, you should use the `GenericValueCast::to_generic` method.
    /// To convert the return value from a `GenericValue`, you should use the `GenericValueCast::from_generic` method.
    fn run_function(&'a self, function: &'a Function, args: &[GenericValue<'a>]) -> GenericValue<'a> {
        let ptr = args.as_ptr() as *mut LLVMGenericValueRef;
        unsafe { engine::LLVMRunFunction(self.into(), function.into(), args.len() as c_uint, ptr).into() }
    }
    /// Returns a pointer to the global value given.
    ///
    /// This is marked as unsafe because the type cannot be guranteed to be the same as the
    /// type of the global value at this point.
    unsafe fn get_global<T>(&'a self, global: &'a Value) -> &'a T {
        mem::transmute(engine::LLVMGetPointerToGlobal(self.into(), global.into()))
    }
    /// Returns a pointer to the global value with the name given.
    ///
    /// This is marked as unsafe because the type cannot be guranteed to be the same as the
    /// type of the global value at this point.
    unsafe fn find_global<T>(&'a self, name: &str) -> Option<&'a T> {
        util::with_cstr(name, |ptr|
            mem::transmute(engine::LLVMGetGlobalValueAddress(self.into(), ptr))
        )
    }
}

/// The options to pass to the MCJIT backend.
#[derive(Copy, Clone)]
pub struct JitOptions {
    /// The degree to which optimizations should be done, between 0 and 3.
    ///
    /// 0 represents no optimizations, 3 represents maximum optimization
    pub opt_level: usize
}
/// The MCJIT backend, which compiles functions and values into machine code.
pub struct JitEngine(PhantomData<[u8]>);
native_ref!{&JitEngine = LLVMExecutionEngineRef}
dispose!{JitEngine, LLVMOpaqueExecutionEngine, LLVMDisposeExecutionEngine}
impl<'a> JitEngine {
    /// Run the closure `cb` with the machine code for the function `function`.
    ///
    /// This will check that the types match at runtime when in debug mode, but not release mode.
    /// You should make sure to use debug mode if you want it to error when the types don't match.
    pub fn with_function<C, A, R>(&self, function: &'a Function, cb: C) where A:Compile<'a>, R:Compile<'a>, C:FnOnce(extern "C" fn (A) -> R) {
        if cfg!(not(ndebug)) {
            let ctx = function.get_context();
            let sig = function.get_signature();
            assert_eq!(Type::get::<R>(ctx), sig.get_return());
            let arg = Type::get::<A>(ctx);
            assert_eq!(sig.get_params(), if let Some(args) = StructType::cast(arg) {
                args.get_elements()
            } else {
                vec![arg]
            });
        }
        unsafe {
            cb(self.get_function::<A, R>(function));
        }
    }
    /// Run the closure `cb` with the machine code for the function `function`.
    pub unsafe fn with_function_unchecked<C, A, R>(&self, function: &'a Function, cb: C) where A:Compile<'a>, R:Compile<'a>, C:FnOnce(extern fn(A) -> R) {
        cb(self.get_function::<A, R>(function));
    }
    /// Returns a pointer to the machine code for the function `function`.
    ///
    /// This is marked as unsafe because the types given as arguments and return could be different
    /// from their internal representation.
    pub unsafe fn get_function<A, R>(&self, function: &'a Function) -> extern fn(A) -> R {
        let ptr:&u8 = self.get_global(function);
        mem::transmute(ptr)
    }
}
impl<'a> ExecutionEngine<'a> for JitEngine {
    type Options = JitOptions;
    fn new(module: &'a Module, options: JitOptions) -> Result<CSemiBox<'a, JitEngine>, CBox<str>> {
        unsafe {
            let mut ee = mem::uninitialized();
            let mut out = mem::zeroed();
            engine::LLVMLinkInMCJIT();
            if target::LLVM_InitializeNativeTarget() == 1 {
                return Err("failed to initialize native target".into())
            }
            if target::LLVM_InitializeNativeAsmPrinter() == 1 {
                return Err("failed to initialize native asm printer".into())
            }
            let mut options = LLVMMCJITCompilerOptions {
                OptLevel: options.opt_level as c_uint,
                CodeModel: LLVMCodeModel::LLVMCodeModelDefault,
                NoFramePointerElim: 0,
                EnableFastISel: 1,
                MCJMM: ptr::null_mut()
            };
            let size = mem::size_of::<LLVMMCJITCompilerOptions>();
            let result = engine::LLVMCreateMCJITCompilerForModule(&mut ee, (&*module).into(), &mut options, size, &mut out);
            if result == 0 {
                Ok(ee.into())
            } else {
                Err(CBox::new(out))
            }
        }
    }
}
/// The interpreter backend
pub struct Interpreter(PhantomData<[u8]>);
native_ref!{&Interpreter = LLVMExecutionEngineRef}
dispose!{Interpreter, LLVMOpaqueExecutionEngine, LLVMDisposeExecutionEngine}
impl<'a> Interpreter {
    /// Run `function` with the arguments given as ``GenericValue`s, then return the result as one.
    ///
    /// To convert the arguments to `GenericValue`s, you should use the `GenericValueCast::to_generic` method.
    /// To convert the return value from a `GenericValue`, you should use the `GenericValueCast::from_generic` method.
    pub fn run_function(&self, function: &'a Function, args: &[GenericValue<'a>]) -> GenericValue<'a> {
        let ptr = args.as_ptr() as *mut LLVMGenericValueRef;
        unsafe { engine::LLVMRunFunction(self.into(), function.into(), args.len() as c_uint, ptr).into() }
    }
}
impl<'a> ExecutionEngine<'a> for Interpreter {
    type Options = ();
    fn new(module: &'a Module, _: ()) -> Result<CSemiBox<'a, Interpreter>, CBox<str>> {
        unsafe {
            let mut ee = mem::uninitialized();
            let mut out = mem::zeroed();
            engine::LLVMLinkInInterpreter();
            let result = engine::LLVMCreateInterpreterForModule(&mut ee, (&*module).into(), &mut out);
            if result == 0 {
                Ok(ee.into())
            } else {
                Err(CBox::new(out))
            }
        }
    }
}
/// A wrapped value that can be passed to an interpreted function or returned from one
pub struct GenericValue<'a> {
    value: LLVMGenericValueRef,
    marker: PhantomData<&'a ()>
}
native_ref!(contra GenericValue, value: LLVMGenericValueRef);
impl<'a> Drop for GenericValue<'a> {
    fn drop(&mut self) {
        unsafe {
            engine::LLVMDisposeGenericValue(self.value)
        }
    }
}

/// A value that can be cast into a `GenericValue` and that a `GenericValue` can be cast into.
///
/// Both these methods require contexts because some `Type` constructors are needed for the
/// conversion and these constructors need a context.
pub trait GenericValueCast<'a> {
    /// Create a `GenericValue` from this value.
    fn to_generic(self, context: &'a Context) -> GenericValue<'a>;
    /// Convert the `GenericValue` into a value of this type again.
    fn from_generic(value: GenericValue<'a>, context: &'a Context) -> Self;
}

impl<'a> GenericValueCast<'a> for f64 {
    fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
        unsafe {
            let ty = core::LLVMDoubleTypeInContext(ctx.into());
            engine::LLVMCreateGenericValueOfFloat(ty, self).into()
        }
    }
    fn from_generic(value: GenericValue<'a>, ctx: &'a Context) -> f64 {
        unsafe {
            let ty = core::LLVMDoubleTypeInContext(ctx.into());
            engine::LLVMGenericValueToFloat(ty, value.into())
        }
    }
}
impl<'a> GenericValueCast<'a> for f32 {
    fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
        unsafe {
            let ty = core::LLVMFloatTypeInContext(ctx.into());
            engine::LLVMCreateGenericValueOfFloat(ty, self as f64).into()
        }
    }
    fn from_generic(value: GenericValue<'a>, ctx: &'a Context) -> f32 {
        unsafe {
            let ty = core::LLVMFloatTypeInContext(ctx.into());
            engine::LLVMGenericValueToFloat(ty, value.into()) as f32
        }
    }
}
macro_rules! generic_int(
    ($ty:ty, $signed:expr) => (
        impl<'a> GenericValueCast<'a> for $ty {
            fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
                unsafe {
                    let ty = <Self as Compile<'a>>::get_type(ctx);
                    engine::LLVMCreateGenericValueOfInt(ty.into(), self as c_ulonglong, $signed as c_int).into()
                }
            }
            fn from_generic(value: GenericValue<'a>, _: &'a Context) -> $ty {
                unsafe {
                    engine::LLVMGenericValueToInt(value.into(), $signed as c_int) as $ty
                }
            }
        }
    );
    (some $signed:ty, $unsigned:ty) => (
        generic_int!{$signed, true}
        generic_int!{$unsigned, false}
    );
);

impl<'a> GenericValueCast<'a> for bool {
    fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
        unsafe {
            let ty = <Self as Compile<'a>>::get_type(ctx);
            engine::LLVMCreateGenericValueOfInt(ty.into(), self as c_ulonglong, 0).into()
        }
    }
    fn from_generic(value: GenericValue<'a>, _: &'a Context) -> bool {
        unsafe {
            engine::LLVMGenericValueToInt(value.into(), 0) != 0
        }
    }
}
generic_int!{some i8, u8}
generic_int!{some i16, u16}
generic_int!{some i32, u32}
generic_int!{some i64, u64}
<commit_msg>Add more detail to with_function doc<commit_after>use libc::{c_int, c_uint, c_ulonglong};
use ffi::{core, target};
use ffi::execution_engine as engine;
use ffi::execution_engine::*;
use ffi::target_machine::LLVMCodeModel;
use cbox::{CBox, CSemiBox, DisposeRef};
use std::marker::PhantomData;
use std::{mem, ptr};
use std::ops::*;
use compile::Compile;
use context::{Context, GetContext};
use module::Module;
use ty::{StructType, Type};
use util::{self, CastFrom};
use value::{Function, Value};

/// An abstract interface for implementation execution of LLVM modules.
///
/// This is designed to support both interpreter and just-in-time (JIT) compiler implementations.
pub trait ExecutionEngine<'a>:'a + Sized + DisposeRef where LLVMExecutionEngineRef: From<&'a Self> {
    /// The options given to the engine upon creation.
    type Options : Copy;
    /// Create a new execution engine with the given `Module` and optiions, or return a
    /// description of the error.
    fn new(module: &'a Module, options: Self::Options) -> Result<CSemiBox<'a, Self>, CBox<str>>;

    /// Add a module to the list of modules to interpret or compile.
    fn add_module(&'a self, module: &'a Module) {
        unsafe { engine::LLVMAddModule(self.into(), (&*module).into()) }
    }
    /// Remove a module from the list of modules to interpret or compile.
    fn remove_module(&'a self, module: &'a Module) -> &'a Module {
        unsafe {
            let mut out = mem::uninitialized();
            engine::LLVMRemoveModule(self.into(), module.into(), &mut out, ptr::null_mut());
            out.into()
        }
    }
    /// Execute all of the static constructors for this program.
    fn run_static_constructors(&'a self) {
        unsafe { engine::LLVMRunStaticConstructors(self.into()) }
    }
    /// Execute all of the static destructors for this program.
    fn run_static_destructors(&'a self) {
        unsafe { engine::LLVMRunStaticDestructors(self.into()) }
    }
    /// Attempt to find a function with the name given, or `None` if there wasn't
    /// a function with that name.
    fn find_function(&'a self, name: &str) -> Option<&'a Function> {
        util::with_cstr(name, |c_name| unsafe {
            let mut out = mem::zeroed();
            engine::LLVMFindFunction(self.into(), c_name, &mut out);
            mem::transmute(out)
        })
    }
    /// Run `function` with the arguments given as ``GenericValue`s, then return the result as one.
    ///
    /// Note that if this engine is a `JitEngine`, it only supports a small fraction of combinations
    /// for the arguments and return value, so be warned.
    ///
    /// To convert the arguments to `GenericValue`s, you should use the `GenericValueCast::to_generic` method.
    /// To convert the return value from a `GenericValue`, you should use the `GenericValueCast::from_generic` method.
    fn run_function(&'a self, function: &'a Function, args: &[GenericValue<'a>]) -> GenericValue<'a> {
        let ptr = args.as_ptr() as *mut LLVMGenericValueRef;
        unsafe { engine::LLVMRunFunction(self.into(), function.into(), args.len() as c_uint, ptr).into() }
    }
    /// Returns a pointer to the global value given.
    ///
    /// This is marked as unsafe because the type cannot be guranteed to be the same as the
    /// type of the global value at this point.
    unsafe fn get_global<T>(&'a self, global: &'a Value) -> &'a T {
        mem::transmute(engine::LLVMGetPointerToGlobal(self.into(), global.into()))
    }
    /// Returns a pointer to the global value with the name given.
    ///
    /// This is marked as unsafe because the type cannot be guranteed to be the same as the
    /// type of the global value at this point.
    unsafe fn find_global<T>(&'a self, name: &str) -> Option<&'a T> {
        util::with_cstr(name, |ptr|
            mem::transmute(engine::LLVMGetGlobalValueAddress(self.into(), ptr))
        )
    }
}

/// The options to pass to the MCJIT backend.
#[derive(Copy, Clone)]
pub struct JitOptions {
    /// The degree to which optimizations should be done, between 0 and 3.
    ///
    /// 0 represents no optimizations, 3 represents maximum optimization
    pub opt_level: usize
}
/// The MCJIT backend, which compiles functions and values into machine code.
pub struct JitEngine(PhantomData<[u8]>);
native_ref!{&JitEngine = LLVMExecutionEngineRef}
dispose!{JitEngine, LLVMOpaqueExecutionEngine, LLVMDisposeExecutionEngine}
impl<'a> JitEngine {
    /// Run the closure `cb` with the machine code for the function `function`.
    ///
    /// If the function takes multiple arguments, these should be wrapped in a tuple due to
    /// the limitations of Rust's type system.
    ///
    /// This will check that the types match at runtime when in debug mode, but not release mode.
    /// You should make sure to use debug mode if you want it to error when the types don't match.
    pub fn with_function<C, A, R>(&self, function: &'a Function, cb: C) where A:Compile<'a>, R:Compile<'a>, C:FnOnce(extern "C" fn (A) -> R) {
        if cfg!(not(ndebug)) {
            let ctx = function.get_context();
            let sig = function.get_signature();
            assert_eq!(Type::get::<R>(ctx), sig.get_return());
            let arg = Type::get::<A>(ctx);
            assert_eq!(sig.get_params(), if let Some(args) = StructType::cast(arg) {
                args.get_elements()
            } else {
                vec![arg]
            });
        }
        unsafe {
            cb(self.get_function::<A, R>(function));
        }
    }
    /// Run the closure `cb` with the machine code for the function `function`.
    pub unsafe fn with_function_unchecked<C, A, R>(&self, function: &'a Function, cb: C) where A:Compile<'a>, R:Compile<'a>, C:FnOnce(extern fn(A) -> R) {
        cb(self.get_function::<A, R>(function));
    }
    /// Returns a pointer to the machine code for the function `function`.
    ///
    /// This is marked as unsafe because the types given as arguments and return could be different
    /// from their internal representation.
    pub unsafe fn get_function<A, R>(&self, function: &'a Function) -> extern fn(A) -> R {
        let ptr:&u8 = self.get_global(function);
        mem::transmute(ptr)
    }
}
impl<'a> ExecutionEngine<'a> for JitEngine {
    type Options = JitOptions;
    fn new(module: &'a Module, options: JitOptions) -> Result<CSemiBox<'a, JitEngine>, CBox<str>> {
        unsafe {
            let mut ee = mem::uninitialized();
            let mut out = mem::zeroed();
            engine::LLVMLinkInMCJIT();
            if target::LLVM_InitializeNativeTarget() == 1 {
                return Err("failed to initialize native target".into())
            }
            if target::LLVM_InitializeNativeAsmPrinter() == 1 {
                return Err("failed to initialize native asm printer".into())
            }
            let mut options = LLVMMCJITCompilerOptions {
                OptLevel: options.opt_level as c_uint,
                CodeModel: LLVMCodeModel::LLVMCodeModelDefault,
                NoFramePointerElim: 0,
                EnableFastISel: 1,
                MCJMM: ptr::null_mut()
            };
            let size = mem::size_of::<LLVMMCJITCompilerOptions>();
            let result = engine::LLVMCreateMCJITCompilerForModule(&mut ee, (&*module).into(), &mut options, size, &mut out);
            if result == 0 {
                Ok(ee.into())
            } else {
                Err(CBox::new(out))
            }
        }
    }
}
/// The interpreter backend
pub struct Interpreter(PhantomData<[u8]>);
native_ref!{&Interpreter = LLVMExecutionEngineRef}
dispose!{Interpreter, LLVMOpaqueExecutionEngine, LLVMDisposeExecutionEngine}
impl<'a> Interpreter {
    /// Run `function` with the arguments given as ``GenericValue`s, then return the result as one.
    ///
    /// To convert the arguments to `GenericValue`s, you should use the `GenericValueCast::to_generic` method.
    /// To convert the return value from a `GenericValue`, you should use the `GenericValueCast::from_generic` method.
    pub fn run_function(&self, function: &'a Function, args: &[GenericValue<'a>]) -> GenericValue<'a> {
        let ptr = args.as_ptr() as *mut LLVMGenericValueRef;
        unsafe { engine::LLVMRunFunction(self.into(), function.into(), args.len() as c_uint, ptr).into() }
    }
}
impl<'a> ExecutionEngine<'a> for Interpreter {
    type Options = ();
    fn new(module: &'a Module, _: ()) -> Result<CSemiBox<'a, Interpreter>, CBox<str>> {
        unsafe {
            let mut ee = mem::uninitialized();
            let mut out = mem::zeroed();
            engine::LLVMLinkInInterpreter();
            let result = engine::LLVMCreateInterpreterForModule(&mut ee, (&*module).into(), &mut out);
            if result == 0 {
                Ok(ee.into())
            } else {
                Err(CBox::new(out))
            }
        }
    }
}
/// A wrapped value that can be passed to an interpreted function or returned from one
pub struct GenericValue<'a> {
    value: LLVMGenericValueRef,
    marker: PhantomData<&'a ()>
}
native_ref!(contra GenericValue, value: LLVMGenericValueRef);
impl<'a> Drop for GenericValue<'a> {
    fn drop(&mut self) {
        unsafe {
            engine::LLVMDisposeGenericValue(self.value)
        }
    }
}

/// A value that can be cast into a `GenericValue` and that a `GenericValue` can be cast into.
///
/// Both these methods require contexts because some `Type` constructors are needed for the
/// conversion and these constructors need a context.
pub trait GenericValueCast<'a> {
    /// Create a `GenericValue` from this value.
    fn to_generic(self, context: &'a Context) -> GenericValue<'a>;
    /// Convert the `GenericValue` into a value of this type again.
    fn from_generic(value: GenericValue<'a>, context: &'a Context) -> Self;
}

impl<'a> GenericValueCast<'a> for f64 {
    fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
        unsafe {
            let ty = core::LLVMDoubleTypeInContext(ctx.into());
            engine::LLVMCreateGenericValueOfFloat(ty, self).into()
        }
    }
    fn from_generic(value: GenericValue<'a>, ctx: &'a Context) -> f64 {
        unsafe {
            let ty = core::LLVMDoubleTypeInContext(ctx.into());
            engine::LLVMGenericValueToFloat(ty, value.into())
        }
    }
}
impl<'a> GenericValueCast<'a> for f32 {
    fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
        unsafe {
            let ty = core::LLVMFloatTypeInContext(ctx.into());
            engine::LLVMCreateGenericValueOfFloat(ty, self as f64).into()
        }
    }
    fn from_generic(value: GenericValue<'a>, ctx: &'a Context) -> f32 {
        unsafe {
            let ty = core::LLVMFloatTypeInContext(ctx.into());
            engine::LLVMGenericValueToFloat(ty, value.into()) as f32
        }
    }
}
macro_rules! generic_int(
    ($ty:ty, $signed:expr) => (
        impl<'a> GenericValueCast<'a> for $ty {
            fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
                unsafe {
                    let ty = <Self as Compile<'a>>::get_type(ctx);
                    engine::LLVMCreateGenericValueOfInt(ty.into(), self as c_ulonglong, $signed as c_int).into()
                }
            }
            fn from_generic(value: GenericValue<'a>, _: &'a Context) -> $ty {
                unsafe {
                    engine::LLVMGenericValueToInt(value.into(), $signed as c_int) as $ty
                }
            }
        }
    );
    (some $signed:ty, $unsigned:ty) => (
        generic_int!{$signed, true}
        generic_int!{$unsigned, false}
    );
);

impl<'a> GenericValueCast<'a> for bool {
    fn to_generic(self, ctx: &'a Context) -> GenericValue<'a> {
        unsafe {
            let ty = <Self as Compile<'a>>::get_type(ctx);
            engine::LLVMCreateGenericValueOfInt(ty.into(), self as c_ulonglong, 0).into()
        }
    }
    fn from_generic(value: GenericValue<'a>, _: &'a Context) -> bool {
        unsafe {
            engine::LLVMGenericValueToInt(value.into(), 0) != 0
        }
    }
}
generic_int!{some i8, u8}
generic_int!{some i16, u16}
generic_int!{some i32, u32}
generic_int!{some i64, u64}
<|endoftext|>


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

use iron::status;
use iron::prelude::*;
use serde_json;
use std::error::Error;
use std::fmt::{self, Debug};

#[derive(Debug)]
struct StringError(pub String);

impl fmt::Display for StringError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

impl Error for StringError {
    fn description(&self) -> &str {
        &*self.0
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ErrorBody {
    pub code: u16,
    pub errno: u16,
    pub error: String,
}

pub struct EndpointError;

impl EndpointError {
    pub fn with(status: status::Status, errno: u16) -> IronResult<Response> {
        let error = status.canonical_reason().unwrap().to_owned();
        let body = ErrorBody {
            code: status.to_u16(),
            errno: errno,
            error: error.clone(),
        };

        Err(IronError::new(StringError(error),
                           (status, serde_json::to_string(&body).unwrap())))
    }
}
<commit_msg>Test for EndpointError<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use iron::status;
use iron::prelude::*;
use serde_json;
use std::error::Error;
use std::fmt::{self, Debug};

#[derive(Debug)]
struct StringError(pub String);

impl fmt::Display for StringError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

impl Error for StringError {
    fn description(&self) -> &str {
        &*self.0
    }
}

#[derive(Debug, Serialize)]
pub struct ErrorBody {
    pub code: u16,
    pub errno: u16,
    pub error: String,
}

pub struct EndpointError;

impl EndpointError {
    pub fn with(status: status::Status, errno: u16) -> IronResult<Response> {
        let error = status.canonical_reason().unwrap().to_owned();
        let body = ErrorBody {
            code: status.to_u16(),
            errno: errno,
            error: error.clone(),
        };

        Err(IronError::new(StringError(error),
                           (status, serde_json::to_string(&body).unwrap())))
    }
}

#[test]
fn test_error() {
    let s_error = StringError(status::BadRequest.canonical_reason().unwrap().to_owned());
    let error = format!("{} {}", s_error, s_error.description());
    assert_eq!(error, r#"StringError("Bad Request") Bad Request"#);
}<|endoftext|>


<commit_before>//! Function approximation and value function representation module.
use core::Shared;
use geometry::Vector;

extern crate lfa;
use self::lfa::approximators::{Multi, Simple};
pub use self::lfa::*;

mod table;
pub use self::table::Table;

pub type SimpleLFA<I, P> = LFA<I, P, Simple>;
pub type MultiLFA<I, P> = LFA<I, P, Multi>;

pub type SharedVFunction<I> = Shared<VFunction<I, Value = f64>>;
pub type SharedQFunction<I> = Shared<QFunction<I, Value = Vector<f64>>>;

/// An interface for state-value functions.
pub trait VFunction<I: ?Sized>: Approximator<I, Value = f64> {
    #[allow(unused_variables)]
    fn evaluate_phi(&self, phi: &Projection) -> f64 { unimplemented!() }

    #[allow(unused_variables)]
    fn update_phi(&mut self, phi: &Projection, update: f64) { unimplemented!() }
}

impl<I: ?Sized, P: Projector<I>> VFunction<I> for SimpleLFA<I, P> {
    fn evaluate_phi(&self, phi: &Projection) -> f64 { self.approximator.evaluate(phi).unwrap() }

    fn update_phi(&mut self, phi: &Projection, update: f64) {
        let _ = self.approximator.update(phi, update);
    }
}

/// An interface for action-value functions.
pub trait QFunction<I: ?Sized>: Approximator<I, Value = Vector<f64>> {
    fn n_actions(&self) -> usize;

    fn evaluate_action(&self, input: &I, action: usize) -> f64 {
        self.evaluate(input).unwrap()[action]
    }

    #[allow(unused_variables)]
    fn update_action(&mut self, input: &I, action: usize, update: f64) { unimplemented!() }

    #[allow(unused_variables)]
    fn evaluate_phi(&self, phi: &Projection) -> Vector<f64> { unimplemented!() }

    #[allow(unused_variables)]
    fn evaluate_action_phi(&self, phi: &Projection, action: usize) -> f64 { unimplemented!() }

    #[allow(unused_variables)]
    fn update_phi(&mut self, phi: &Projection, updates: Vector<f64>) { unimplemented!() }

    #[allow(unused_variables)]
    fn update_action_phi(&mut self, phi: &Projection, action: usize, update: f64) {
        unimplemented!()
    }
}

impl<I: ?Sized, P: Projector<I>> QFunction<I> for MultiLFA<I, P> {
    fn n_actions(&self) -> usize { self.approximator.weights.cols() }

    fn evaluate_action(&self, input: &I, action: usize) -> f64 {
        let p = self.projector.project(input);

        self.evaluate_action_phi(&p, action)
    }

    fn update_action(&mut self, input: &I, action: usize, update: f64) {
        let p = self.projector.project(input);

        self.update_action_phi(&p, action, update);
    }

    fn evaluate_phi(&self, phi: &Projection) -> Vector<f64> {
        self.approximator.evaluate(&phi).unwrap()
    }

    fn evaluate_action_phi(&self, phi: &Projection, action: usize) -> f64 {
        let col = self.approximator.weights.column(action);

        match phi {
            &Projection::Dense(ref dense) => col.dot(&(dense / phi.z())),
            &Projection::Sparse(ref sparse) => sparse.iter().fold(0.0, |acc, idx| acc + col[*idx]),
        }
    }

    fn update_phi(&mut self, phi: &Projection, updates: Vector<f64>) {
        let _ = self.approximator.update(phi, updates);
    }

    fn update_action_phi(&mut self, phi: &Projection, action: usize, update: f64) {
        let mut col = self.approximator.weights.column_mut(action);

        let z = phi.z();
        let scaled_update = update / z;

        match phi {
            &Projection::Dense(ref dense) => col.scaled_add(scaled_update, dense),
            &Projection::Sparse(ref sparse) => for idx in sparse {
                col[*idx] += scaled_update
            },
        }
    }
}
<commit_msg>chore(fa): constrain imports from "lfa" in rsrl::fa<commit_after>//! Function approximation and value function representation module.
use core::Shared;
use geometry::Vector;

extern crate lfa;
use self::lfa::approximators::{Multi, Simple};
pub use self::lfa::{
    Approximator,
    Parameterised,
    LFA,

    Projector,
    Projection,
    projectors,

    EvaluationResult,
    UpdateResult,
    AdaptResult,
};

mod table;
pub use self::table::Table;

pub type SimpleLFA<I, P> = LFA<I, P, Simple>;
pub type MultiLFA<I, P> = LFA<I, P, Multi>;

pub type SharedVFunction<I> = Shared<VFunction<I, Value = f64>>;
pub type SharedQFunction<I> = Shared<QFunction<I, Value = Vector<f64>>>;

/// An interface for state-value functions.
pub trait VFunction<I: ?Sized>: Approximator<I, Value = f64> {
    #[allow(unused_variables)]
    fn evaluate_phi(&self, phi: &Projection) -> f64 { unimplemented!() }

    #[allow(unused_variables)]
    fn update_phi(&mut self, phi: &Projection, update: f64) { unimplemented!() }
}

impl<I: ?Sized, P: Projector<I>> VFunction<I> for SimpleLFA<I, P> {
    fn evaluate_phi(&self, phi: &Projection) -> f64 { self.approximator.evaluate(phi).unwrap() }

    fn update_phi(&mut self, phi: &Projection, update: f64) {
        let _ = self.approximator.update(phi, update);
    }
}

/// An interface for action-value functions.
pub trait QFunction<I: ?Sized>: Approximator<I, Value = Vector<f64>> {
    fn n_actions(&self) -> usize;

    fn evaluate_action(&self, input: &I, action: usize) -> f64 {
        self.evaluate(input).unwrap()[action]
    }

    #[allow(unused_variables)]
    fn update_action(&mut self, input: &I, action: usize, update: f64) { unimplemented!() }

    #[allow(unused_variables)]
    fn evaluate_phi(&self, phi: &Projection) -> Vector<f64> { unimplemented!() }

    #[allow(unused_variables)]
    fn evaluate_action_phi(&self, phi: &Projection, action: usize) -> f64 { unimplemented!() }

    #[allow(unused_variables)]
    fn update_phi(&mut self, phi: &Projection, updates: Vector<f64>) { unimplemented!() }

    #[allow(unused_variables)]
    fn update_action_phi(&mut self, phi: &Projection, action: usize, update: f64) {
        unimplemented!()
    }
}

impl<I: ?Sized, P: Projector<I>> QFunction<I> for MultiLFA<I, P> {
    fn n_actions(&self) -> usize { self.approximator.weights.cols() }

    fn evaluate_action(&self, input: &I, action: usize) -> f64 {
        let p = self.projector.project(input);

        self.evaluate_action_phi(&p, action)
    }

    fn update_action(&mut self, input: &I, action: usize, update: f64) {
        let p = self.projector.project(input);

        self.update_action_phi(&p, action, update);
    }

    fn evaluate_phi(&self, phi: &Projection) -> Vector<f64> {
        self.approximator.evaluate(&phi).unwrap()
    }

    fn evaluate_action_phi(&self, phi: &Projection, action: usize) -> f64 {
        let col = self.approximator.weights.column(action);

        match phi {
            &Projection::Dense(ref dense) => col.dot(&(dense / phi.z())),
            &Projection::Sparse(ref sparse) => sparse.iter().fold(0.0, |acc, idx| acc + col[*idx]),
        }
    }

    fn update_phi(&mut self, phi: &Projection, updates: Vector<f64>) {
        let _ = self.approximator.update(phi, updates);
    }

    fn update_action_phi(&mut self, phi: &Projection, action: usize, update: f64) {
        let mut col = self.approximator.weights.column_mut(action);

        let z = phi.z();
        let scaled_update = update / z;

        match phi {
            &Projection::Dense(ref dense) => col.scaled_add(scaled_update, dense),
            &Projection::Sparse(ref sparse) => for idx in sparse {
                col[*idx] += scaled_update
            },
        }
    }
}
<|endoftext|>


<commit_before>//! The `filter` element.

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
use std::slice::Iter;

use crate::coord_units::CoordUnits;
use crate::document::{AcquiredNodes, NodeId};
use crate::drawing_ctx::ViewParams;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::{ElementError, ValueErrorKind};
use crate::length::*;
use crate::node::NodeBorrow;
use crate::parsers::{Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::rect::Rect;
use crate::xml::Attributes;

/// The <filter> node.
pub struct Filter {
    x: Length<Horizontal>,
    y: Length<Vertical>,
    width: ULength<Horizontal>,
    height: ULength<Vertical>,
    filterunits: CoordUnits,
    primitiveunits: CoordUnits,
}

impl Default for Filter {
    /// Constructs a new `Filter` with default properties.
    fn default() -> Self {
        Self {
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
            width: ULength::<Horizontal>::parse_str("120%").unwrap(),
            height: ULength::<Vertical>::parse_str("120%").unwrap(),
            filterunits: CoordUnits::ObjectBoundingBox,
            primitiveunits: CoordUnits::UserSpaceOnUse,
        }
    }
}

impl Filter {
    pub fn get_filter_units(&self) -> CoordUnits {
        self.filterunits
    }

    pub fn get_primitive_units(&self) -> CoordUnits {
        self.primitiveunits
    }

    pub fn get_rect(&self, values: &ComputedValues, params: &ViewParams) -> Rect {
        // With filterunits == ObjectBoundingBox, lengths represent fractions or percentages of the
        // referencing node. No units are allowed (it's checked during attribute parsing).
        let (x, y, w, h) = if self.filterunits == CoordUnits::ObjectBoundingBox {
            (
                self.x.length,
                self.y.length,
                self.width.length,
                self.height.length,
            )
        } else {
            (
                self.x.normalize(values, &params),
                self.y.normalize(values, &params),
                self.width.normalize(values, &params),
                self.height.normalize(values, &params),
            )
        };

        Rect::new(x, y, x + w, y + h)
    }
}

impl SetAttributes for Filter {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        // Parse filterUnits first as it affects x, y, width, height checks.
        let result = attrs
            .iter()
            .find(|(attr, _)| attr.expanded() == expanded_name!("", "filterUnits"))
            .and_then(|(attr, value)| attr.parse(value).ok());
        if let Some(filter_units) = result {
            self.filterunits = filter_units
        }

        // With ObjectBoundingBox, only fractions and percents are allowed.
        let units_allowed = self.filterunits != CoordUnits::ObjectBoundingBox;

        // Parse the rest of the attributes.
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => {
                    self.x = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "y") => {
                    self.y = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "width") => {
                    self.width = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "height") => {
                    self.height = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "primitiveUnits") => self.primitiveunits = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

fn check_units<N: Normalize, V: Validate>(
    length: CssLength<N, V>,
    units_allowed: bool,
    attr: QualName,
) -> Result<CssLength<N, V>, ElementError> {
    if units_allowed {
        return Ok(length);
    }

    match length.unit {
        LengthUnit::Px | LengthUnit::Percent => Ok(length),
        _ => Err(ElementError {
            attr,
            err: ValueErrorKind::parse_error(
                "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
            ),
        }),
    }
}

impl Draw for Filter {}

#[derive(Debug, Clone, PartialEq)]
pub enum FilterValue {
    Url(NodeId),
    // TODO: add functions from https://www.w3.org/TR/filter-effects-1/#filter-functions
}

#[derive(Debug, Clone, PartialEq)]
pub struct FilterValueList(Vec<FilterValue>);

impl Default for FilterValueList {
    fn default() -> FilterValueList {
        FilterValueList(vec![])
    }
}

impl FilterValueList {
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn iter(&self) -> Iter<'_, FilterValue> {
        self.0.iter()
    }

    /// Check that at least one filter URI exists and that all contained
    /// URIs reference existing <filter> elements.
    ///
    /// The `node_name` refers to the node being filtered; it is just
    /// to log an error in case the filter value list is not
    /// applicable.
    pub fn is_applicable(&self, node_name: &str, acquired_nodes: &mut AcquiredNodes<'_>) -> bool {
        if self.is_empty() {
            return false;
        }

        self.iter()
            .map(|v| match v {
                FilterValue::Url(v) => v,
            })
            .all(|v| match acquired_nodes.acquire(v) {
                Ok(acquired) => {
                    let filter_node = acquired.get();

                    match *filter_node.borrow_element() {
                        Element::Filter(_) => true,
                        _ => {
                            rsvg_log!(
                                "element {} will not be filtered since \"{}\" is not a filter",
                                node_name,
                                v,
                            );
                            false
                        }
                    }
                }
                _ => {
                    rsvg_log!(
                        "element {} will not be filtered since its filter \"{}\" was not found",
                        node_name,
                        v,
                    );
                    false
                }
            })
    }
}

impl Parse for FilterValueList {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let mut result = FilterValueList::default();

        loop {
            let loc = parser.current_source_location();

            let url = parser.expect_url()?;
            let node_id =
                NodeId::parse(&url).map_err(|e| loc.new_custom_error(ValueErrorKind::from(e)))?;
            result.0.push(FilterValue::Url(node_id));

            if parser.is_exhausted() {
                break;
            }
        }

        Ok(result)
    }
}

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

    #[test]
    fn parses_filter_value_list() {
        let n1 = NodeId::External("foo.svg".to_string(), "bar".to_string());
        let n2 = NodeId::External("test.svg".to_string(), "baz".to_string());
        assert_eq!(
            FilterValueList::parse_str("url(foo.svg#bar) url(test.svg#baz)").unwrap(),
            FilterValueList(vec![FilterValue::Url(n1), FilterValue::Url(n2)])
        );
    }

    #[test]
    fn detects_invalid_filter_value_list() {
        assert!(FilterValueList::parse_str("none").is_err());
        assert!(FilterValueList::parse_str("").is_err());
        assert!(FilterValueList::parse_str("fail").is_err());
        assert!(FilterValueList::parse_str("url(#test) none").is_err());
    }
}
<commit_msg>Filter: rename a couple of fields<commit_after>//! The `filter` element.

use cssparser::Parser;
use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
use std::slice::Iter;

use crate::coord_units::CoordUnits;
use crate::document::{AcquiredNodes, NodeId};
use crate::drawing_ctx::ViewParams;
use crate::element::{Draw, Element, ElementResult, SetAttributes};
use crate::error::{ElementError, ValueErrorKind};
use crate::length::*;
use crate::node::NodeBorrow;
use crate::parsers::{Parse, ParseValue};
use crate::properties::ComputedValues;
use crate::rect::Rect;
use crate::xml::Attributes;

/// The <filter> node.
pub struct Filter {
    x: Length<Horizontal>,
    y: Length<Vertical>,
    width: ULength<Horizontal>,
    height: ULength<Vertical>,
    filter_units: CoordUnits,
    primitive_units: CoordUnits,
}

impl Default for Filter {
    /// Constructs a new `Filter` with default properties.
    fn default() -> Self {
        Self {
            x: Length::<Horizontal>::parse_str("-10%").unwrap(),
            y: Length::<Vertical>::parse_str("-10%").unwrap(),
            width: ULength::<Horizontal>::parse_str("120%").unwrap(),
            height: ULength::<Vertical>::parse_str("120%").unwrap(),
            filter_units: CoordUnits::ObjectBoundingBox,
            primitive_units: CoordUnits::UserSpaceOnUse,
        }
    }
}

impl Filter {
    pub fn get_filter_units(&self) -> CoordUnits {
        self.filter_units
    }

    pub fn get_primitive_units(&self) -> CoordUnits {
        self.primitive_units
    }

    pub fn get_rect(&self, values: &ComputedValues, params: &ViewParams) -> Rect {
        // With filterunits == ObjectBoundingBox, lengths represent fractions or percentages of the
        // referencing node. No units are allowed (it's checked during attribute parsing).
        let (x, y, w, h) = if self.filter_units == CoordUnits::ObjectBoundingBox {
            (
                self.x.length,
                self.y.length,
                self.width.length,
                self.height.length,
            )
        } else {
            (
                self.x.normalize(values, &params),
                self.y.normalize(values, &params),
                self.width.normalize(values, &params),
                self.height.normalize(values, &params),
            )
        };

        Rect::new(x, y, x + w, y + h)
    }
}

impl SetAttributes for Filter {
    fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
        // Parse filterUnits first as it affects x, y, width, height checks.
        let result = attrs
            .iter()
            .find(|(attr, _)| attr.expanded() == expanded_name!("", "filterUnits"))
            .and_then(|(attr, value)| attr.parse(value).ok());
        if let Some(filter_units) = result {
            self.filter_units = filter_units
        }

        // With ObjectBoundingBox, only fractions and percents are allowed.
        let units_allowed = self.filter_units != CoordUnits::ObjectBoundingBox;

        // Parse the rest of the attributes.
        for (attr, value) in attrs.iter() {
            match attr.expanded() {
                expanded_name!("", "x") => {
                    self.x = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "y") => {
                    self.y = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "width") => {
                    self.width = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "height") => {
                    self.height = attr
                        .parse(value)
                        .and_then(|v| check_units(v, units_allowed, attr))?
                }
                expanded_name!("", "primitiveUnits") => self.primitive_units = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}

fn check_units<N: Normalize, V: Validate>(
    length: CssLength<N, V>,
    units_allowed: bool,
    attr: QualName,
) -> Result<CssLength<N, V>, ElementError> {
    if units_allowed {
        return Ok(length);
    }

    match length.unit {
        LengthUnit::Px | LengthUnit::Percent => Ok(length),
        _ => Err(ElementError {
            attr,
            err: ValueErrorKind::parse_error(
                "unit identifiers are not allowed with filterUnits set to objectBoundingBox",
            ),
        }),
    }
}

impl Draw for Filter {}

#[derive(Debug, Clone, PartialEq)]
pub enum FilterValue {
    Url(NodeId),
    // TODO: add functions from https://www.w3.org/TR/filter-effects-1/#filter-functions
}

#[derive(Debug, Clone, PartialEq)]
pub struct FilterValueList(Vec<FilterValue>);

impl Default for FilterValueList {
    fn default() -> FilterValueList {
        FilterValueList(vec![])
    }
}

impl FilterValueList {
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    pub fn iter(&self) -> Iter<'_, FilterValue> {
        self.0.iter()
    }

    /// Check that at least one filter URI exists and that all contained
    /// URIs reference existing <filter> elements.
    ///
    /// The `node_name` refers to the node being filtered; it is just
    /// to log an error in case the filter value list is not
    /// applicable.
    pub fn is_applicable(&self, node_name: &str, acquired_nodes: &mut AcquiredNodes<'_>) -> bool {
        if self.is_empty() {
            return false;
        }

        self.iter()
            .map(|v| match v {
                FilterValue::Url(v) => v,
            })
            .all(|v| match acquired_nodes.acquire(v) {
                Ok(acquired) => {
                    let filter_node = acquired.get();

                    match *filter_node.borrow_element() {
                        Element::Filter(_) => true,
                        _ => {
                            rsvg_log!(
                                "element {} will not be filtered since \"{}\" is not a filter",
                                node_name,
                                v,
                            );
                            false
                        }
                    }
                }
                _ => {
                    rsvg_log!(
                        "element {} will not be filtered since its filter \"{}\" was not found",
                        node_name,
                        v,
                    );
                    false
                }
            })
    }
}

impl Parse for FilterValueList {
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
        let mut result = FilterValueList::default();

        loop {
            let loc = parser.current_source_location();

            let url = parser.expect_url()?;
            let node_id =
                NodeId::parse(&url).map_err(|e| loc.new_custom_error(ValueErrorKind::from(e)))?;
            result.0.push(FilterValue::Url(node_id));

            if parser.is_exhausted() {
                break;
            }
        }

        Ok(result)
    }
}

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

    #[test]
    fn parses_filter_value_list() {
        let n1 = NodeId::External("foo.svg".to_string(), "bar".to_string());
        let n2 = NodeId::External("test.svg".to_string(), "baz".to_string());
        assert_eq!(
            FilterValueList::parse_str("url(foo.svg#bar) url(test.svg#baz)").unwrap(),
            FilterValueList(vec![FilterValue::Url(n1), FilterValue::Url(n2)])
        );
    }

    #[test]
    fn detects_invalid_filter_value_list() {
        assert!(FilterValueList::parse_str("none").is_err());
        assert!(FilterValueList::parse_str("").is_err());
        assert!(FilterValueList::parse_str("fail").is_err());
        assert!(FilterValueList::parse_str("url(#test) none").is_err());
    }
}
<|endoftext|>


<commit_before>use std::io::{self, BufRead, BufReader};
use std::net::TcpStream;
use std::path::PathBuf;
use std::thread;

use ssh2;
use serde_json;

use futures::stream::BoxStream;
use futures::sync::mpsc::{channel, Sender};
use futures::{Future, Sink, Stream};

use bot;

/// Gerrit username
pub type Username = String;

#[derive(Deserialize, Debug)]
pub struct User {
    pub name: Option<String>,
    pub username: Username,
    pub email: Option<String>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Approval {
    #[serde(rename = "type")]
    pub approval_type: String,
    pub description: String,
    pub value: String,
    pub old_value: Option<String>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PatchSet {
    pub number: String,
    pub revision: String,
    pub parents: Vec<String>,
    #[serde(rename = "ref")]
    pub reference: String,
    pub uploader: User,
    pub created_on: u32,
    pub author: User,
    pub is_draft: bool,
    pub kind: String,
    pub size_insertions: i32,
    pub size_deletions: i32,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Change {
    pub project: String,
    pub branch: String,
    pub id: String,
    pub number: String,
    pub subject: String,
    pub owner: User,
    pub url: String,
    pub commit_message: String,
    pub status: String,
}

#[derive(Deserialize, Debug, Eq, PartialEq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct ChangeKey {
    pub id: String,
}

// Only specific events are accepted by this type by design!
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Event {
    pub author: Option<User>,
    pub uploader: Option<User>,
    pub approvals: Option<Vec<Approval>>,
    pub comment: Option<String>,
    #[serde(rename = "patchSet")]
    pub patchset: PatchSet,
    pub change: Change,
    pub project: String,
    #[serde(rename = "refName")]
    pub ref_name: String,
    #[serde(rename = "changeKey")]
    pub changekey: ChangeKey,
    #[serde(rename = "type")]
    pub event_type: String,
    #[serde(rename = "eventCreatedOn")]
    pub created_on: u32,
}

impl Event {
    pub fn into_action(self) -> bot::Action {
        if self.approvals.is_some() {
            return bot::Action::UpdateApprovals(self);
        }
        bot::Action::NoOp
    }
}

#[derive(Debug)]
pub enum StreamError {
    Io(io::Error),
    Parse(serde_json::Error),
    Terminated,
}

impl From<io::Error> for StreamError {
    fn from(err: io::Error) -> StreamError {
        StreamError::Io(err)
    }
}

impl From<serde_json::Error> for StreamError {
    fn from(err: serde_json::Error) -> StreamError {
        StreamError::Parse(err)
    }
}

fn get_pub_key_path(priv_key_path: &PathBuf) -> PathBuf {
    let mut pub_key_path = PathBuf::from(priv_key_path.to_str().unwrap());
    pub_key_path.set_extension("pub");
    pub_key_path
}

fn send_terminate_msg(tx: Sender<Result<String, ()>>) {
    // we terminate our stream by sending an empty err message
    let _ = tx.send(Err(())).wait();
}

pub fn event_stream(
    host: &str,
    port: u16,
    username: String,
    priv_key_path: PathBuf,
) -> BoxStream<Event, StreamError> {
    let hostport = format!("{}:{}", host, port);
    let pub_key_path = get_pub_key_path(&priv_key_path);
    println!(
        "[D] Will use public key: {}",
        pub_key_path.to_str().unwrap()
    );

    let (main_tx, rx) = channel(1);
    thread::spawn(move || {
        loop {
            println!("[I] (Re)connecting to Gerrit over ssh: {}", hostport);

            // Connect to the local SSH server
            let mut session = match ssh2::Session::new() {
                Some(session) => session,
                None => {
                    println!("[E] Could not create a new ssh session for connecting to Gerrit.");
                    send_terminate_msg(main_tx);
                    return;
                }
            };

            let tcp = match TcpStream::connect(&hostport) {
                Ok(tcp) => tcp,
                Err(err) => {
                    println!("[E] Could not connect to gerrit at {}: {:?}", hostport, err);
                    send_terminate_msg(main_tx);
                    return;
                }
            };

            if let Err(err) = session.handshake(&tcp) {
                println!("[E] Could not connect to gerrit: {:?}", err);
                send_terminate_msg(main_tx);
                return;
            };

            // Try to authenticate
            if let Err(err) = session.userauth_pubkey_file(
                &username,
                Some(&pub_key_path),
                &priv_key_path,
                None,
            )
            {
                println!("[E] Could not authenticate: {:?}", err);
                send_terminate_msg(main_tx);
                return;
            };

            let mut ssh_channel = match session.channel_session() {
                Ok(ssh_channel) => ssh_channel,
                Err(err) => {
                    println!("[E] Could not create ssh channel: {:?}", err);
                    send_terminate_msg(main_tx);
                    return;
                }
            };

            if let Err(err) = ssh_channel.exec("gerrit stream-events") {
                println!(
                    "[E] Could not execture gerrit stream-event command over ssh: {:?}",
                    err
                );
                send_terminate_msg(main_tx);
                return;
            };

            let buf_channel = BufReader::new(ssh_channel);
            let mut tx = main_tx.clone();
            for line in buf_channel.lines() {
                if let Ok(line) = line {
                    match tx.clone().send(Ok(line)).wait() {
                        Ok(s) => tx = s,
                        Err(err) => {
                            println!("[E] Cannot send message through channel {:?}", err);
                            break;
                        }
                    }
                } else {
                    println!("[E] Could not read line from buffer. Will drop connection.");
                    break;
                }
            }
        }
    });

    rx.then(|event| match event.unwrap() {
        Ok(event) => {
            let json: String = event;
            let res = serde_json::from_str(&json);
            println!("[D] {:?} for json: {}", res, json);
            Ok(res.ok())
        }
        Err(_) => Err(StreamError::Terminated),
    }).filter_map(|event| event)
        .boxed()
}

#[cfg(test)]
mod test {
    use super::{get_pub_key_path, PathBuf};

    #[test]
    fn test_get_pub_key_path() {
        let result = get_pub_key_path(&PathBuf::from("some_priv_key"));
        assert!(result == PathBuf::from("some_priv_key.pub"));
    }
}
<commit_msg>Listen only on comments in gerrit.<commit_after>use std::io::{self, BufRead, BufReader};
use std::net::TcpStream;
use std::path::PathBuf;
use std::thread;

use ssh2;
use serde_json;

use futures::stream::BoxStream;
use futures::sync::mpsc::{channel, Sender};
use futures::{Future, Sink, Stream};

use bot;

/// Gerrit username
pub type Username = String;

#[derive(Deserialize, Debug)]
pub struct User {
    pub name: Option<String>,
    pub username: Username,
    pub email: Option<String>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Approval {
    #[serde(rename = "type")]
    pub approval_type: String,
    pub description: String,
    pub value: String,
    pub old_value: Option<String>,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PatchSet {
    pub number: String,
    pub revision: String,
    pub parents: Vec<String>,
    #[serde(rename = "ref")]
    pub reference: String,
    pub uploader: User,
    pub created_on: u32,
    pub author: User,
    pub is_draft: bool,
    pub kind: String,
    pub size_insertions: i32,
    pub size_deletions: i32,
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Change {
    pub project: String,
    pub branch: String,
    pub id: String,
    pub number: String,
    pub subject: String,
    pub owner: User,
    pub url: String,
    pub commit_message: String,
    pub status: String,
}

#[derive(Deserialize, Debug, Eq, PartialEq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct ChangeKey {
    pub id: String,
}

// Only specific events are accepted by this type by design!
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Event {
    pub author: Option<User>,
    pub uploader: Option<User>,
    pub approvals: Option<Vec<Approval>>,
    pub comment: Option<String>,
    #[serde(rename = "patchSet")]
    pub patchset: PatchSet,
    pub change: Change,
    pub project: String,
    #[serde(rename = "refName")]
    pub ref_name: String,
    #[serde(rename = "changeKey")]
    pub changekey: ChangeKey,
    #[serde(rename = "type")]
    pub event_type: String,
    #[serde(rename = "eventCreatedOn")]
    pub created_on: u32,
}

impl Event {
    pub fn into_action(self) -> bot::Action {
        if self.approvals.is_some() {
            return bot::Action::UpdateApprovals(self);
        }
        bot::Action::NoOp
    }
}

#[derive(Debug)]
pub enum StreamError {
    Io(io::Error),
    Parse(serde_json::Error),
    Terminated,
}

impl From<io::Error> for StreamError {
    fn from(err: io::Error) -> StreamError {
        StreamError::Io(err)
    }
}

impl From<serde_json::Error> for StreamError {
    fn from(err: serde_json::Error) -> StreamError {
        StreamError::Parse(err)
    }
}

fn get_pub_key_path(priv_key_path: &PathBuf) -> PathBuf {
    let mut pub_key_path = PathBuf::from(priv_key_path.to_str().unwrap());
    pub_key_path.set_extension("pub");
    pub_key_path
}

fn send_terminate_msg(tx: Sender<Result<String, ()>>) {
    // we terminate our stream by sending an empty err message
    let _ = tx.send(Err(())).wait();
}

pub fn event_stream(
    host: &str,
    port: u16,
    username: String,
    priv_key_path: PathBuf,
) -> BoxStream<Event, StreamError> {
    let hostport = format!("{}:{}", host, port);
    let pub_key_path = get_pub_key_path(&priv_key_path);
    println!(
        "[D] Will use public key: {}",
        pub_key_path.to_str().unwrap()
    );

    let (main_tx, rx) = channel(1);
    thread::spawn(move || {
        loop {
            println!("[I] (Re)connecting to Gerrit over ssh: {}", hostport);

            // Connect to the local SSH server
            let mut session = match ssh2::Session::new() {
                Some(session) => session,
                None => {
                    println!("[E] Could not create a new ssh session for connecting to Gerrit.");
                    send_terminate_msg(main_tx);
                    return;
                }
            };

            let tcp = match TcpStream::connect(&hostport) {
                Ok(tcp) => tcp,
                Err(err) => {
                    println!("[E] Could not connect to gerrit at {}: {:?}", hostport, err);
                    send_terminate_msg(main_tx);
                    return;
                }
            };

            if let Err(err) = session.handshake(&tcp) {
                println!("[E] Could not connect to gerrit: {:?}", err);
                send_terminate_msg(main_tx);
                return;
            };

            // Try to authenticate
            if let Err(err) = session.userauth_pubkey_file(
                &username,
                Some(&pub_key_path),
                &priv_key_path,
                None,
            )
            {
                println!("[E] Could not authenticate: {:?}", err);
                send_terminate_msg(main_tx);
                return;
            };

            let mut ssh_channel = match session.channel_session() {
                Ok(ssh_channel) => ssh_channel,
                Err(err) => {
                    println!("[E] Could not create ssh channel: {:?}", err);
                    send_terminate_msg(main_tx);
                    return;
                }
            };

            if let Err(err) = ssh_channel.exec("gerrit stream-events -s comment-added") {
                println!(
                    "[E] Could not execture gerrit stream-event command over ssh: {:?}",
                    err
                );
                send_terminate_msg(main_tx);
                return;
            };

            let buf_channel = BufReader::new(ssh_channel);
            let mut tx = main_tx.clone();
            for line in buf_channel.lines() {
                if let Ok(line) = line {
                    match tx.clone().send(Ok(line)).wait() {
                        Ok(s) => tx = s,
                        Err(err) => {
                            println!("[E] Cannot send message through channel {:?}", err);
                            break;
                        }
                    }
                } else {
                    println!("[E] Could not read line from buffer. Will drop connection.");
                    break;
                }
            }
        }
    });

    rx.then(|event| match event.unwrap() {
        Ok(event) => {
            let json: String = event;
            let res = serde_json::from_str(&json);
            println!("[D] {:?} for json: {}", res, json);
            Ok(res.ok())
        }
        Err(_) => Err(StreamError::Terminated),
    }).filter_map(|event| event)
        .boxed()
}

#[cfg(test)]
mod test {
    use super::{get_pub_key_path, PathBuf};

    #[test]
    fn test_get_pub_key_path() {
        let result = get_pub_key_path(&PathBuf::from("some_priv_key"));
        assert!(result == PathBuf::from("some_priv_key.pub"));
    }
}
<|endoftext|>


<commit_before>use iron::prelude::*;
use persistent::{Write, State};
use rand::{thread_rng, Rng};

use rustorm::pool::ManagedPool;
use iron::typemap::Key;
use window_service::window::Window;
use rustorm::table::Table;
use std::collections::BTreeMap;
use rustorm::pool::Platform;
use rustorm::database::DbError;
use rustorm::database::Database;
use std::sync::{Arc,Mutex, RwLock};

pub struct GlobalPools{
	pub cache_map: BTreeMap<String, Cache>, //caches indexed by db_url
}

impl Key for GlobalPools{
	type Value = GlobalPools;
}

impl GlobalPools{

	/// initialize the pools
	pub fn new()->Self{
		GlobalPools{
			cache_map: BTreeMap::new()
		}
	}

	pub fn from_request(req: &mut Request)->Arc<RwLock<Self>>{
		let global = req.get::<State<GlobalPools>>().unwrap();
		global
	}

	pub fn has_cache(&self, db_url: &str)->bool{
		self.cache_map.contains_key(db_url)
	}
	pub fn has_cached_tables(&self, db_url: &str)->bool{
		match self.get_cache(db_url){
			Some(cache) => cache.tables.is_some(),
			None => false
		}
	}

	pub fn get_cache(&self, db_url: &str)->Option<&Cache>{
		self.cache_map.get(db_url)
	}
	pub fn get_cached_tables(&self, db_url: &str)->Option<Vec<Table>>{
		match self.get_cache(db_url){
			Some(cache) => {
				match cache.tables{
					Some(ref tables) => Some(tables.clone()),
					None => None
				}
			}
			None => None
		}
	}

	pub fn has_cached_windows(&self, db_url: &str)->bool{
		match self.get_cache(db_url){
			Some(cache) => cache.windows.is_some(),
			None => false
		}
	}
	pub fn get_cached_windows(&self, db_url: &str)->Option<Vec<Window>>{
		match self.get_cache(db_url){
			Some(cache) => {
				match cache.windows{
					Some(ref windows) => Some(windows.clone()),
					None => None
				}
			}
			None => None
		}
	}
    /// cache this window values to this db_url
	pub fn cache_windows(&mut self, db_url: &str, windows: Vec<Window>){
		if self.has_cache(db_url) {
			let mut cache =  self.cache_map.remove(db_url).unwrap();
			cache.set_windows(windows);
			self.cache_map.insert(db_url.to_owned(), cache);
		}
		else{
			let mut cache = Cache::new(db_url);
			cache.set_windows(windows);
			self.cache_map.insert(db_url.to_owned(), cache);
		}
    }

	pub fn get_connection(&mut self, db_url: &str)->Result<Platform, DbError>{
		if self.has_cache(db_url){
			let platform = self.get_cache(db_url).unwrap().get_connection();
			Ok(platform)
		}else{
			let cache = Cache::new(db_url);
			self.cache_map.insert(db_url.to_owned(), cache);
			//try again
			self.get_connection(db_url)
		}
	}

	pub fn cache_tables(&mut self, db_url: &str, tables: Vec<Table>){
		if self.has_cache(db_url){
			let mut cache = self.cache_map.remove(db_url).unwrap();
			cache.set_tables(tables);
			self.cache_map.insert(db_url.to_owned(), cache);
		}else{
			let mut cache = Cache::new(db_url);
			cache.set_tables(tables);
			self.cache_map.insert(db_url.to_owned(), cache);
		}
	}

}

/// items cached, unique for each db_url connection
pub struct Cache{
	/// connections are cached here as well
	pub managed_pool: ManagedPool,
    /// windows extraction is an expensive operation and doesn't change very often
	/// None indicates, that nothing is cached yet, empty can be indicated as cached
    pub windows: Option<Vec<Window>>,
    /// tables extraction is an expensive operation and doesn't change very often
    pub tables: Option<Vec<Table>>,
}

impl Cache{

	fn new(db_url:&str)->Self{
		let pool = ManagedPool::init(db_url, 100).unwrap();
		Cache{
			managed_pool: pool,
			windows: None,
			tables: None,
		}
	}

	fn set_windows(&mut self, windows: Vec<Window>){
		self.windows = Some(windows);
	}
	fn set_tables(&mut self, tables: Vec<Table>){
		self.tables = Some(tables);
	}

	pub fn get_connection(&self)->Platform{
		self.managed_pool.connect().unwrap()
	}
}

// the db_url is stored in the headers
pub fn get_db_url(req: &Request)->Option<String>{
	let db_url: Option<&[Vec<u8>]> = req.headers.get_raw("db_url");
	match db_url{
		Some(db_url) => {
			let first = &db_url[0];
			let url = String::from_utf8(first.clone()).unwrap();
			Some(url)
		},
		None => None
	}
}

<commit_msg>Changed pool connections to 20<commit_after>use iron::prelude::*;
use persistent::{Write, State};
use rand::{thread_rng, Rng};

use rustorm::pool::ManagedPool;
use iron::typemap::Key;
use window_service::window::Window;
use rustorm::table::Table;
use std::collections::BTreeMap;
use rustorm::pool::Platform;
use rustorm::database::DbError;
use rustorm::database::Database;
use std::sync::{Arc,Mutex, RwLock};

pub struct GlobalPools{
	pub cache_map: BTreeMap<String, Cache>, //caches indexed by db_url
}

impl Key for GlobalPools{
	type Value = GlobalPools;
}

impl GlobalPools{

	/// initialize the pools
	pub fn new()->Self{
		GlobalPools{
			cache_map: BTreeMap::new()
		}
	}

	pub fn from_request(req: &mut Request)->Arc<RwLock<Self>>{
		let global = req.get::<State<GlobalPools>>().unwrap();
		global
	}

	pub fn has_cache(&self, db_url: &str)->bool{
		self.cache_map.contains_key(db_url)
	}
	pub fn has_cached_tables(&self, db_url: &str)->bool{
		match self.get_cache(db_url){
			Some(cache) => cache.tables.is_some(),
			None => false
		}
	}

	pub fn get_cache(&self, db_url: &str)->Option<&Cache>{
		self.cache_map.get(db_url)
	}
	pub fn get_cached_tables(&self, db_url: &str)->Option<Vec<Table>>{
		match self.get_cache(db_url){
			Some(cache) => {
				match cache.tables{
					Some(ref tables) => Some(tables.clone()),
					None => None
				}
			}
			None => None
		}
	}

	pub fn has_cached_windows(&self, db_url: &str)->bool{
		match self.get_cache(db_url){
			Some(cache) => cache.windows.is_some(),
			None => false
		}
	}
	pub fn get_cached_windows(&self, db_url: &str)->Option<Vec<Window>>{
		match self.get_cache(db_url){
			Some(cache) => {
				match cache.windows{
					Some(ref windows) => Some(windows.clone()),
					None => None
				}
			}
			None => None
		}
	}
    /// cache this window values to this db_url
	pub fn cache_windows(&mut self, db_url: &str, windows: Vec<Window>){
		if self.has_cache(db_url) {
			let mut cache =  self.cache_map.remove(db_url).unwrap();
			cache.set_windows(windows);
			self.cache_map.insert(db_url.to_owned(), cache);
		}
		else{
			let mut cache = Cache::new(db_url);
			cache.set_windows(windows);
			self.cache_map.insert(db_url.to_owned(), cache);
		}
    }

	pub fn get_connection(&mut self, db_url: &str)->Result<Platform, DbError>{
		if self.has_cache(db_url){
			let platform = self.get_cache(db_url).unwrap().get_connection();
			Ok(platform)
		}else{
			let cache = Cache::new(db_url);
			self.cache_map.insert(db_url.to_owned(), cache);
			//try again
			self.get_connection(db_url)
		}
	}

	pub fn cache_tables(&mut self, db_url: &str, tables: Vec<Table>){
		if self.has_cache(db_url){
			let mut cache = self.cache_map.remove(db_url).unwrap();
			cache.set_tables(tables);
			self.cache_map.insert(db_url.to_owned(), cache);
		}else{
			let mut cache = Cache::new(db_url);
			cache.set_tables(tables);
			self.cache_map.insert(db_url.to_owned(), cache);
		}
	}

}

/// items cached, unique for each db_url connection
pub struct Cache{
	/// connections are cached here as well
	pub managed_pool: ManagedPool,
    /// windows extraction is an expensive operation and doesn't change very often
	/// None indicates, that nothing is cached yet, empty can be indicated as cached
    pub windows: Option<Vec<Window>>,
    /// tables extraction is an expensive operation and doesn't change very often
    pub tables: Option<Vec<Table>>,
}

impl Cache{

	fn new(db_url:&str)->Self{
		let pool = ManagedPool::init(db_url,20).unwrap();
		Cache{
			managed_pool: pool,
			windows: None,
			tables: None,
		}
	}

	fn set_windows(&mut self, windows: Vec<Window>){
		self.windows = Some(windows);
	}
	fn set_tables(&mut self, tables: Vec<Table>){
		self.tables = Some(tables);
	}

	pub fn get_connection(&self)->Platform{
		self.managed_pool.connect().unwrap()
	}
}

// the db_url is stored in the headers
pub fn get_db_url(req: &Request)->Option<String>{
	let db_url: Option<&[Vec<u8>]> = req.headers.get_raw("db_url");
	match db_url{
		Some(db_url) => {
			let first = &db_url[0];
			let url = String::from_utf8(first.clone()).unwrap();
			Some(url)
		},
		None => None
	}
}

<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Result;

use io::OPEN_BUS;
use io::IO;
use memory::MemSegment;
use util::ShiftRegister8;

pub struct FM2IO {
    x: u8,
}

impl FM2IO {
    pub fn read(file: String) -> Result<FM2IO> {
        return Ok( FM2IO {
                x: 0
            } );
    }
}

impl MemSegment for FM2IO {
        fn read(&mut self, idx: u16) -> u8 {
        match idx {
            0x4016 => OPEN_BUS,// | self.controller1.shift(),
            0x4017 => OPEN_BUS,// | self.controller2.shift(),
            x => invalid_address!(x),
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        match idx {
            0x4016 => (),
            0x4017 => (),
            x => invalid_address!(x),
        }
    }
}

impl IO for FM2IO {
    fn poll(&mut self) {
        //TODO
    }
}<commit_msg>Implement basic FM2 movie support.<commit_after>use std::fs::File;
use std::io::Result;
use std::io::BufReader;
use std::io::BufRead;
use std::iter::Iterator;

use io::OPEN_BUS;
use io::IO;
use memory::MemSegment;
use util::ShiftRegister8;

pub struct FM2IO {
    iter: Box<Iterator<Item=String>>,
    controller1: ShiftRegister8,
    controller2: ShiftRegister8,
}

impl FM2IO {
    pub fn read(file: String) -> Result<FM2IO> {
        let file = try!(File::open(file));
        let reader = BufReader::new(file);
        let iter = reader.lines()
            .map(|result| result.unwrap())
            .skip_while(|line| !line.contains("|"))
            .skip(1);
        return Ok( FM2IO {
                iter: Box::new(iter),
                controller1: ShiftRegister8::new(0),
                controller2: ShiftRegister8::new(0),
            } );
    }
}

fn parse(string: &str) -> u8 {
    let mut accumulator: u8 = 0;
    for (idx, _) in string.char_indices()
        .filter(|&(_, c)| c != '.') {
        accumulator |= 1 << (7 - idx);
    }
    accumulator
}

impl MemSegment for FM2IO {
        fn read(&mut self, idx: u16) -> u8 {
        match idx {
            0x4016 => OPEN_BUS | self.controller1.shift(),
            0x4017 => OPEN_BUS | self.controller2.shift(),
            x => invalid_address!(x),
        }
    }

    fn write(&mut self, idx: u16, val: u8) {
        match idx {
            0x4016 => {
                if val & 0x01 != 0 {
                    if let Some(line) = self.iter.next() {
                        let mut split = line.split("|").skip(1)
                            .skip(1); //Ignore the commands for now.
                        self.controller1.load(parse(split.next().unwrap()));
                        self.controller2.load(parse(split.next().unwrap()));
                    }
                }
            },
            0x4017 => (),
            x => invalid_address!(x),
        }
    }
}

impl IO for FM2IO {
    fn poll(&mut self) {
        //TODO
    }
}<|endoftext|>