<commit_before>extern crate libc;

use libc::*;
use std::slice;
use std::convert::{From, Into};
use std::mem::transmute;
use std::iter::repeat;

#[macro_export]
macro_rules! mex_entry {
    ( $func: ident ) => {
        use std::slice;

        #[no_mangle]
        #[allow(non_snake_case)]
        pub fn mexFunction(nlhs: i32, lhs: *mut Array,
                           nrhs: i32, rhs: *const Array) -> () {
            let lhs_slice = unsafe { slice::from_raw_parts_mut(lhs, nlhs as usize) };
            let rhs_slice = unsafe { slice::from_raw_parts(rhs, nrhs as usize) };
            $func(lhs_slice, rhs_slice);
        }
    }
}

pub struct Array {
    ptr: *mut c_void,
}

impl Array {
    pub fn new(dims: Vec<usize>, class: MxClassID, complex: bool) -> Array {
        let ndims_mwsize = dims.len() as MwSize;
        let dims_mwsize: Vec<MwSize> = dims.iter().map(|&x| x as MwSize).collect();
        Array{
            ptr: unsafe { mxCreateNumericArray(ndims_mwsize,
                                               dims_mwsize.as_ptr(),
                                               class.into(),
                                               if complex { 1i32 } else { 0i32 }) }
        }
    }

    pub fn get_class(self: &Self) -> MxClassID {
        MxClassID::from(unsafe { mxGetClassID(self.ptr) })
    }

    pub fn get_dimensions(self: &Self) -> Vec<usize> {
        let num_dims = unsafe { mxGetNumberOfDimensions(self.ptr) } as usize;
        let dims = unsafe { mxGetDimensions(self.ptr) };
        let dims_slice = unsafe {
            slice::from_raw_parts(dims, num_dims)
        };
        dims_slice.iter().map(|&x| x as usize).collect()
    }

    pub fn set_dimensions(self: &mut Self, dims: Vec<usize>) -> () {
        let ndims_mwsize = dims.len() as MwSize;
        let dims_mwsize: Vec<MwSize> = dims.iter().map(|&x| x as MwSize).collect();
        unsafe { mxSetDimensions(self.ptr, dims_mwsize.as_ptr(),
                                           ndims_mwsize); }
    }

    pub fn real_data(self: &Self) -> Data {
        let ptr = unsafe { mxGetData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => Data::Bool(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::F64 => Data::F64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::F32 => Data::F32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I8 => Data::I8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U8 => Data::U8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I16 => Data::I16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U16 => Data::U16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I32 => Data::I32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U32 => Data::U32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I64 => Data::I64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U64 => Data::U64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            _ => Data::NotNumeric
        }
    }

    pub fn imag_data(self: &Self) -> Data {
        let ptr = unsafe { mxGetImagData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => Data::Bool(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::F64 => Data::F64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::F32 => Data::F32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I8 => Data::I8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U8 => Data::U8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I16 => Data::I16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U16 => Data::U16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I32 => Data::I32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U32 => Data::U32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I64 => Data::I64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U64 => Data::U64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            _ => Data::NotNumeric
        }
    }

    pub fn real_data_mut(self: &mut Self) -> DataMut {
        let ptr = unsafe { mxGetData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => DataMut::Bool(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::F64 => DataMut::F64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::F32 => DataMut::F32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I8 => DataMut::I8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U8 => DataMut::U8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I16 => DataMut::I16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U16 => DataMut::U16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I32 => DataMut::I32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U32 => DataMut::U32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I64 => DataMut::I64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U64 => DataMut::U64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            _ => DataMut::NotNumeric
        }
    }

    pub fn imag_data_mut(self: &mut Self) -> DataMut {
        let ptr = unsafe { mxGetImagData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => DataMut::Bool(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::F64 => DataMut::F64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::F32 => DataMut::F32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I8 => DataMut::I8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U8 => DataMut::U8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I16 => DataMut::I16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U16 => DataMut::U16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I32 => DataMut::I32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U32 => DataMut::U32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I64 => DataMut::I64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U64 => DataMut::U64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            _ => DataMut::NotNumeric
        }
    }

    pub fn as_string(self: &Self) -> String {
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        let mut buf: Vec<u8> = repeat(0u8).take(numel+1).collect();
        unsafe {
            mxGetString(self.ptr,
                        transmute(buf.as_mut_ptr()),
                        numel as MwSize + 1);
        }
        buf.pop(); // remove trailing \0
        String::from_utf8(buf).ok().unwrap()
    }
}

pub enum DataMut<'a> {
    NotNumeric,

    Bool(&'a mut [bool]),

    F64(&'a mut [f64]),
    F32(&'a mut [f32]),

    I8(&'a mut [i8]),
    U8(&'a mut [u8]),

    I16(&'a mut [i16]),
    U16(&'a mut [u16]),

    I32(&'a mut [i32]),
    U32(&'a mut [u32]),

    I64(&'a mut [i64]),
    U64(&'a mut [u64]),
}

pub enum Data<'a> {
    NotNumeric,

    Bool(&'a [bool]),

    F64(&'a [f64]),
    F32(&'a [f32]),

    I8(&'a [i8]),
    U8(&'a [u8]),

    I16(&'a [i16]),
    U16(&'a [u16]),

    I32(&'a [i32]),
    U32(&'a [u32]),

    I64(&'a [i64]),
    U64(&'a [u64]),
}

#[cfg(octave)]
pub type MwSize = u32;

#[cfg(matlab)]
pub type MwSize = usize;

#[derive(Debug, Copy, Clone)]
pub enum MxClassID {
    Unknown,
    Cell,
    Struct,
    Bool,
    Char,
    Unused,
    F64,
    F32,
    I8,
    U8,
    I16,
    U16,
    I32,
    U32,
    I64,
    U64,
    Function
}

impl From<i32> for MxClassID {
    fn from(id: i32) -> MxClassID {
        use MxClassID::*;
        match id {
            0 => Unknown,
            1 => Cell,
            2 => Struct,
            3 => Bool,
            4 => Char,
            5 => Unused,
            6 => F64,
            7 => F32,
            8 => I8,
            9 => U8,
            10 => I16,
            11 => U16,
            12 => I32,
            13 => U32,
            14 => I64,
            15 => U64,
            16 => Function,
            _ => Unknown // or error?
        }
    }
}

impl Into<i32> for MxClassID {
    fn into(self: Self) -> i32 {
        use MxClassID::*;
        match self {
            Unknown => 0,
            Cell => 1,
            Struct => 2,
            Bool => 3,
            Char => 4,
            Unused => 5,
            F64 => 6,
            F32 => 7,
            I8 => 8,
            U8 => 9,
            I16 => 10,
            U16 => 11,
            I32 => 12,
            U32 => 13,
            I64 => 14,
            U64 => 15,
            Function => 16
        }
    }
}

#[cfg(octave)]
#[link(name = "octave")]
#[link(name = "octinterp")]
extern { }

#[cfg(matlab)]
#[link(name = "mex")]
#[link(name = "mat")]
extern { }

extern {
    // type checks
    fn mxGetClassID(ptr: *const c_void) -> i32;

    // creation
    fn mxCreateNumericArray(ndim: MwSize, dims: *const MwSize,
                            class: i32, complex: i32) -> *mut c_void;

    // dimension interrogation
    fn mxGetDimensions(ptr: *const c_void) -> *const MwSize;
    fn mxGetNumberOfDimensions(ptr: *const c_void) -> MwSize;

    // dimension setting
    fn mxSetDimensions(ptr: *mut c_void, dims: *const MwSize, ndims: MwSize) -> ();

    // data extractors
    fn mxGetData(ptr: *const c_void) -> *mut c_void;
    fn mxGetImagData(ptr: *const c_void) -> *mut c_void;
    fn mxGetString(ptr: *const c_void, s: *mut char, strlen: MwSize) -> i32;
}

<commit_msg>mex_println<commit_after>extern crate libc;

use libc::*;
use std::slice;
use std::convert::{From, Into};
use std::mem::transmute;
use std::iter::repeat;
use std::ffi::CString;

#[macro_export]
macro_rules! mex_entry {
    ( $func: ident ) => {
        use std::slice;

        #[no_mangle]
        #[allow(non_snake_case)]
        pub fn mexFunction(nlhs: i32, lhs: *mut Array,
                           nrhs: i32, rhs: *const Array) -> () {
            let lhs_slice = unsafe { slice::from_raw_parts_mut(lhs, nlhs as usize) };
            let rhs_slice = unsafe { slice::from_raw_parts(rhs, nrhs as usize) };
            $func(lhs_slice, rhs_slice);
        }
    }
}

pub struct Array {
    ptr: *mut c_void,
}

impl Array {
    pub fn new(dims: Vec<usize>, class: MxClassID, complex: bool) -> Array {
        let ndims_mwsize = dims.len() as MwSize;
        let dims_mwsize: Vec<MwSize> = dims.iter().map(|&x| x as MwSize).collect();
        Array{
            ptr: unsafe { mxCreateNumericArray(ndims_mwsize,
                                               dims_mwsize.as_ptr(),
                                               class.into(),
                                               if complex { 1i32 } else { 0i32 }) }
        }
    }

    pub fn get_class(self: &Self) -> MxClassID {
        MxClassID::from(unsafe { mxGetClassID(self.ptr) })
    }

    pub fn get_dimensions(self: &Self) -> Vec<usize> {
        let num_dims = unsafe { mxGetNumberOfDimensions(self.ptr) } as usize;
        let dims = unsafe { mxGetDimensions(self.ptr) };
        let dims_slice = unsafe {
            slice::from_raw_parts(dims, num_dims)
        };
        dims_slice.iter().map(|&x| x as usize).collect()
    }

    pub fn set_dimensions(self: &mut Self, dims: Vec<usize>) -> () {
        let ndims_mwsize = dims.len() as MwSize;
        let dims_mwsize: Vec<MwSize> = dims.iter().map(|&x| x as MwSize).collect();
        unsafe { mxSetDimensions(self.ptr, dims_mwsize.as_ptr(),
                                           ndims_mwsize); }
    }

    pub fn real_data(self: &Self) -> Data {
        let ptr = unsafe { mxGetData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => Data::Bool(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::F64 => Data::F64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::F32 => Data::F32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I8 => Data::I8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U8 => Data::U8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I16 => Data::I16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U16 => Data::U16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I32 => Data::I32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U32 => Data::U32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I64 => Data::I64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U64 => Data::U64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            _ => Data::NotNumeric
        }
    }

    pub fn imag_data(self: &Self) -> Data {
        let ptr = unsafe { mxGetImagData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => Data::Bool(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::F64 => Data::F64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::F32 => Data::F32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I8 => Data::I8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U8 => Data::U8(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I16 => Data::I16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U16 => Data::U16(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I32 => Data::I32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U32 => Data::U32(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            MxClassID::I64 => Data::I64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),
            MxClassID::U64 => Data::U64(unsafe { slice::from_raw_parts(transmute(ptr), numel) }),

            _ => Data::NotNumeric
        }
    }

    pub fn real_data_mut(self: &mut Self) -> DataMut {
        let ptr = unsafe { mxGetData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => DataMut::Bool(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::F64 => DataMut::F64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::F32 => DataMut::F32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I8 => DataMut::I8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U8 => DataMut::U8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I16 => DataMut::I16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U16 => DataMut::U16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I32 => DataMut::I32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U32 => DataMut::U32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I64 => DataMut::I64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U64 => DataMut::U64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            _ => DataMut::NotNumeric
        }
    }

    pub fn imag_data_mut(self: &mut Self) -> DataMut {
        let ptr = unsafe { mxGetImagData(self.ptr) };
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        match self.get_class() {
            MxClassID::Bool => DataMut::Bool(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::F64 => DataMut::F64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::F32 => DataMut::F32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I8 => DataMut::I8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U8 => DataMut::U8(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I16 => DataMut::I16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U16 => DataMut::U16(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I32 => DataMut::I32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U32 => DataMut::U32(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            MxClassID::I64 => DataMut::I64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),
            MxClassID::U64 => DataMut::U64(unsafe { slice::from_raw_parts_mut(transmute(ptr), numel) }),

            _ => DataMut::NotNumeric
        }
    }

    pub fn as_string(self: &Self) -> String {
        let dims = self.get_dimensions();
        let numel = dims.iter().fold(1usize, |l,r| l*r);
        let mut buf: Vec<u8> = repeat(0u8).take(numel+1).collect();
        unsafe {
            mxGetString(self.ptr,
                        transmute(buf.as_mut_ptr()),
                        numel as MwSize + 1);
        }
        buf.pop(); // remove trailing \0
        String::from_utf8(buf).ok().unwrap()
    }
}

pub enum DataMut<'a> {
    NotNumeric,

    Bool(&'a mut [bool]),

    F64(&'a mut [f64]),
    F32(&'a mut [f32]),

    I8(&'a mut [i8]),
    U8(&'a mut [u8]),

    I16(&'a mut [i16]),
    U16(&'a mut [u16]),

    I32(&'a mut [i32]),
    U32(&'a mut [u32]),

    I64(&'a mut [i64]),
    U64(&'a mut [u64]),
}

pub enum Data<'a> {
    NotNumeric,

    Bool(&'a [bool]),

    F64(&'a [f64]),
    F32(&'a [f32]),

    I8(&'a [i8]),
    U8(&'a [u8]),

    I16(&'a [i16]),
    U16(&'a [u16]),

    I32(&'a [i32]),
    U32(&'a [u32]),

    I64(&'a [i64]),
    U64(&'a [u64]),
}

#[cfg(octave)]
pub type MwSize = u32;

#[cfg(matlab)]
pub type MwSize = usize;

#[derive(Debug, Copy, Clone)]
pub enum MxClassID {
    Unknown,
    Cell,
    Struct,
    Bool,
    Char,
    Unused,
    F64,
    F32,
    I8,
    U8,
    I16,
    U16,
    I32,
    U32,
    I64,
    U64,
    Function
}

impl From<i32> for MxClassID {
    fn from(id: i32) -> MxClassID {
        use MxClassID::*;
        match id {
            0 => Unknown,
            1 => Cell,
            2 => Struct,
            3 => Bool,
            4 => Char,
            5 => Unused,
            6 => F64,
            7 => F32,
            8 => I8,
            9 => U8,
            10 => I16,
            11 => U16,
            12 => I32,
            13 => U32,
            14 => I64,
            15 => U64,
            16 => Function,
            _ => Unknown // or error?
        }
    }
}

impl Into<i32> for MxClassID {
    fn into(self: Self) -> i32 {
        use MxClassID::*;
        match self {
            Unknown => 0,
            Cell => 1,
            Struct => 2,
            Bool => 3,
            Char => 4,
            Unused => 5,
            F64 => 6,
            F32 => 7,
            I8 => 8,
            U8 => 9,
            I16 => 10,
            U16 => 11,
            I32 => 12,
            U32 => 13,
            I64 => 14,
            U64 => 15,
            Function => 16
        }
    }
}

#[cfg(octave)]
#[link(name = "octave")]
#[link(name = "octinterp")]
extern { }

#[cfg(matlab)]
#[link(name = "mex")]
#[link(name = "mat")]
extern { }

pub fn mex_println<T: Into<Vec<u8>>>(s: T) -> () {
    let first_arg = CString::new("%s").unwrap();
    let message = CString::new(s).unwrap();
    unsafe {
        mexPrintf(first_arg.as_ptr(), message.as_ptr());
    }
}

extern {
    // type checks
    fn mxGetClassID(ptr: *const c_void) -> i32;

    // creation
    fn mxCreateNumericArray(ndim: MwSize, dims: *const MwSize,
                            class: i32, complex: i32) -> *mut c_void;

    // dimension interrogation
    fn mxGetDimensions(ptr: *const c_void) -> *const MwSize;
    fn mxGetNumberOfDimensions(ptr: *const c_void) -> MwSize;

    // dimension setting
    fn mxSetDimensions(ptr: *mut c_void, dims: *const MwSize, ndims: MwSize) -> ();

    // data extractors
    fn mxGetData(ptr: *const c_void) -> *mut c_void;
    fn mxGetImagData(ptr: *const c_void) -> *mut c_void;
    fn mxGetString(ptr: *const c_void, s: *mut char, strlen: MwSize) -> i32;

    // matlab text output
    fn mexPrintf(msg: *const c_char, ...);
}

<|endoftext|>


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

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Token {
    Output,
    Input,
    Loop,
    EndLoop,
    Move(i32),
    Add(i32, i32),
    Set(i32, i32),
    MulCopy(i32, i32, i32),
    Scan(i32),
    LoadOut(i32, i32),
    LoadOutSet(i32),
    If(i32),
    EndIf,
}
use Token::*;

pub fn parse(code: &str) -> Vec<Token> {
    let mut tokens = Vec::new();
    for i in code.chars() {
        match i {
            '+' => tokens.push(Add(0, 1)),
            '-' => tokens.push(Add(0, -1)),
            '>' => tokens.push(Move(1)),
            '<' => tokens.push(Move(-1)),
            '[' => tokens.push(Loop),
            ']' => tokens.push(EndLoop),
            ',' => tokens.push(Input),
            '.' => {
                tokens.push(LoadOut(0, 0));
                tokens.push(Output);
            },
            _ => ()
        };
    }

    tokens
    }

pub fn optimize(tokens: Vec<Token>) -> Vec<Token> {
    let mut newtokens: Vec<Token> = Vec::new();
    let mut shift = 0;
    let mut do_output = false;
    // With HashMap, the order sometimes switches
    // in recursion, and the optimizer never exits.
    let mut adds: BTreeMap<i32, i32> = BTreeMap::new();
    let mut sets: BTreeMap<i32, i32> = BTreeMap::new();
    let mut pre_loop_sets: BTreeMap<i32, i32> = BTreeMap::new();

    for token in tokens.iter() {
        let mut prev_token = newtokens.last().map(|t| *t);
        if *token == EndLoop && prev_token == Some(Loop) && shift == 0 && adds.contains_key(&0) {
            if adds.len() == 1 {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Set(*offset, *value));
                    }
                    sets.clear();
                    newtokens.push(Set(0, 0));
                    newtokens.push(EndIf);
                } else {
                    sets.insert(0, 0);
                }
                pre_loop_sets.clear();
                adds.clear();
                continue
            } else if adds.get(&0) == Some(&-1) {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Set(*offset, *value));
                    }
                }
                for (offset, value) in adds.iter() {
                    if *offset != 0 {
                        let src = 0;
                        let dest = *offset;
                        let mul = *value;
                        if pre_loop_sets.contains_key(&src) {
                            let val = pre_loop_sets.get(&src).unwrap() * mul;
                            newtokens.push(Add(dest, val));
                        } else {
                            newtokens.push(MulCopy(src, dest, mul));
                        }
                    }
                }
                if !sets.is_empty() {
                    newtokens.push(EndIf);
                }
                pre_loop_sets.clear();
                adds.clear();
                sets.clear();
                sets.insert(0, 0);
                continue
            }
        }

        match *token {
            Loop =>
                for (offset, value) in sets.iter() {
                    pre_loop_sets.insert(*offset+shift, *value);
                },
            Set(_, _) | Add(_, _) | Move(_) => {},
            _ => pre_loop_sets.clear()
        }

        match *token {
            Set(_, _) | Add(_, _) | Move(_) | LoadOut(_, _) | LoadOutSet(_) | Output => {},
            _ => {
               if do_output {
                   newtokens.push(Output);
                   do_output = false;
               }

               for (offset, value) in sets.iter() {
                  newtokens.push(Set(*offset, *value));
               }
               for (offset, value) in adds.iter() {
                  newtokens.push(Add(*offset, *value));
               }
               sets.clear();
               adds.clear();
            }
        }

        if shift != 0 {
           match *token {
               Loop | Input | Scan(_) => {
                   newtokens.push(Move(shift));
                   shift = 0;
               },
               _ => {}
           }
        }

        match *token {
            Set(mut offset, val) => {
                offset += shift;
                // Add before Set does nothing; remove it
                if adds.contains_key(&offset) {
                    adds.remove(&offset);
                }
                sets.insert(offset, val);
            },
            Add(mut offset, mut val) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    val = sets.get(&offset).unwrap() + val;
                    sets.insert(offset, val);
                } else {
                    val = adds.get(&offset).unwrap_or(&0) + val;
                    adds.insert(offset, val);
                }
            },
            MulCopy(src, dest, mul) =>
                newtokens.push(MulCopy(src+shift, dest+shift, mul)),
            // XXX Deal with shift in if, if those are ever generated
            If(offset) =>
                newtokens.push(If(offset+shift)),
            Move(offset) =>
                shift += offset,
            Output =>
                do_output = true,
            LoadOut(mut offset, add) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    newtokens.push(LoadOutSet(sets.get(&offset).unwrap() + add));
                } else {
                    newtokens.push(LoadOut(offset, adds.get(&offset).unwrap_or(&0) + add));
                }
            },
            EndLoop => {
                prev_token = newtokens.last().map(|t| *t);
                if prev_token == Some(Loop) && shift != 0 && sets.is_empty() && adds.is_empty() {
                    newtokens.pop(); // Remove StartLoop
                    newtokens.push(Scan(shift));
                } else {
                    if shift != 0 {
                        newtokens.push(Move(shift));
                    }
                    newtokens.push(EndLoop);
                }
                shift = 0;
            },
            EndIf | LoadOutSet(_) | Loop | Input | Scan(_) =>
                newtokens.push(*token),
        }
    }

    // Any remaining add/set/shift is ignored, as it would have no effect
    if do_output {
        newtokens.push(Output);
    }

    // Optimize recursively
    if newtokens != tokens {
        optimize(newtokens)
    } else {
        newtokens
    }
}
<commit_msg>Remove prev_token variable altogether<commit_after>use std::collections::BTreeMap;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Token {
    Output,
    Input,
    Loop,
    EndLoop,
    Move(i32),
    Add(i32, i32),
    Set(i32, i32),
    MulCopy(i32, i32, i32),
    Scan(i32),
    LoadOut(i32, i32),
    LoadOutSet(i32),
    If(i32),
    EndIf,
}
use Token::*;

pub fn parse(code: &str) -> Vec<Token> {
    let mut tokens = Vec::new();
    for i in code.chars() {
        match i {
            '+' => tokens.push(Add(0, 1)),
            '-' => tokens.push(Add(0, -1)),
            '>' => tokens.push(Move(1)),
            '<' => tokens.push(Move(-1)),
            '[' => tokens.push(Loop),
            ']' => tokens.push(EndLoop),
            ',' => tokens.push(Input),
            '.' => {
                tokens.push(LoadOut(0, 0));
                tokens.push(Output);
            },
            _ => ()
        };
    }

    tokens
    }

pub fn optimize(tokens: Vec<Token>) -> Vec<Token> {
    let mut newtokens: Vec<Token> = Vec::new();
    let mut shift = 0;
    let mut do_output = false;
    // With HashMap, the order sometimes switches
    // in recursion, and the optimizer never exits.
    let mut adds: BTreeMap<i32, i32> = BTreeMap::new();
    let mut sets: BTreeMap<i32, i32> = BTreeMap::new();
    let mut pre_loop_sets: BTreeMap<i32, i32> = BTreeMap::new();

    for token in tokens.iter() {
        if *token == EndLoop && newtokens.last() == Some(&Loop) && shift == 0 && adds.contains_key(&0) {
            if adds.len() == 1 {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Set(*offset, *value));
                    }
                    sets.clear();
                    newtokens.push(Set(0, 0));
                    newtokens.push(EndIf);
                } else {
                    sets.insert(0, 0);
                }
                pre_loop_sets.clear();
                adds.clear();
                continue
            } else if adds.get(&0) == Some(&-1) {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Set(*offset, *value));
                    }
                }
                for (offset, value) in adds.iter() {
                    if *offset != 0 {
                        let src = 0;
                        let dest = *offset;
                        let mul = *value;
                        if pre_loop_sets.contains_key(&src) {
                            let val = pre_loop_sets.get(&src).unwrap() * mul;
                            newtokens.push(Add(dest, val));
                        } else {
                            newtokens.push(MulCopy(src, dest, mul));
                        }
                    }
                }
                if !sets.is_empty() {
                    newtokens.push(EndIf);
                }
                pre_loop_sets.clear();
                adds.clear();
                sets.clear();
                sets.insert(0, 0);
                continue
            }
        }

        match *token {
            Loop =>
                for (offset, value) in sets.iter() {
                    pre_loop_sets.insert(*offset+shift, *value);
                },
            Set(_, _) | Add(_, _) | Move(_) => {},
            _ => pre_loop_sets.clear()
        }

        match *token {
            Set(_, _) | Add(_, _) | Move(_) | LoadOut(_, _) | LoadOutSet(_) | Output => {},
            _ => {
               if do_output {
                   newtokens.push(Output);
                   do_output = false;
               }

               for (offset, value) in sets.iter() {
                  newtokens.push(Set(*offset, *value));
               }
               for (offset, value) in adds.iter() {
                  newtokens.push(Add(*offset, *value));
               }
               sets.clear();
               adds.clear();
            }
        }

        if shift != 0 {
           match *token {
               Loop | Input | Scan(_) => {
                   newtokens.push(Move(shift));
                   shift = 0;
               },
               _ => {}
           }
        }

        match *token {
            Set(mut offset, val) => {
                offset += shift;
                // Add before Set does nothing; remove it
                if adds.contains_key(&offset) {
                    adds.remove(&offset);
                }
                sets.insert(offset, val);
            },
            Add(mut offset, mut val) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    val = sets.get(&offset).unwrap() + val;
                    sets.insert(offset, val);
                } else {
                    val = adds.get(&offset).unwrap_or(&0) + val;
                    adds.insert(offset, val);
                }
            },
            MulCopy(src, dest, mul) =>
                newtokens.push(MulCopy(src+shift, dest+shift, mul)),
            // XXX Deal with shift in if, if those are ever generated
            If(offset) =>
                newtokens.push(If(offset+shift)),
            Move(offset) =>
                shift += offset,
            Output =>
                do_output = true,
            LoadOut(mut offset, add) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    newtokens.push(LoadOutSet(sets.get(&offset).unwrap() + add));
                } else {
                    newtokens.push(LoadOut(offset, adds.get(&offset).unwrap_or(&0) + add));
                }
            },
            EndLoop => {
                if newtokens.last() == Some(&Loop) && shift != 0 && sets.is_empty() && adds.is_empty() {
                    newtokens.pop(); // Remove StartLoop
                    newtokens.push(Scan(shift));
                } else {
                    if shift != 0 {
                        newtokens.push(Move(shift));
                    }
                    newtokens.push(EndLoop);
                }
                shift = 0;
            },
            EndIf | LoadOutSet(_) | Loop | Input | Scan(_) =>
                newtokens.push(*token),
        }
    }

    // Any remaining add/set/shift is ignored, as it would have no effect
    if do_output {
        newtokens.push(Output);
    }

    // Optimize recursively
    if newtokens != tokens {
        optimize(newtokens)
    } else {
        newtokens
    }
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! delegate_method {
    {impl $($tt:tt)*} =>
    {delegate_method!{@impl_collect () $($tt)*}};

    {@impl_collect ($($headtt:tt)*) {$($tailtt:tt)*}} =>
    {delegate_method!{@impl_expansion ($($headtt)*) ($($tailtt)*) ()}};

    {@impl_collect ($($headtt:tt)*) $tt:tt $($tailtt:tt)*} =>
    {delegate_method!{@impl_collect ($($headtt)* $tt) $($tailtt)*}};

    {@impl_expansion_impl ($($headtt:tt)*) ($($itemtt:tt)*) ($($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!{@impl_expansion ($($headtt)*) ($($resttt)*) ($($tailtt)* #[inline] $($itemtt)*)}};

    (@impl_finalization ($($headtt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@as_item #[allow(dead_code)]impl $($headtt)* { $($tailtt)* });};

    (@as_item $i:item) => { $i };

    {@impl_expansion ($($headtt:tt)*) () ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_finalization ($($headtt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     (type $ascty:ident = $dstty:ty; $($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*) ($($resttt)*) ($($tailtt)* type $ascty = $dstty; ));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt $(as $fldty:ty)* : ) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*) () ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld_last:tt $(as $fldty_last:ty)* :
      $fld:tt $(as $fldty:ty)* : $($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*)
                      ($fld $(as $fldty)* : $($resttt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt : $($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*) ($fld as () : $($resttt)*) ($($tailtt)*))};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt as $fldty:ty :
      pub fn $fcn:ident $(< $($lt:tt),* >)* ($($args:tt)*) $(-> $r:ty)* $(where $($aty:ident : $atyb_first:tt $(:: $ayb_first_ext:tt)* $(+ $ayb_rest:tt $(:: $ayb_rest_ext:tt)*)*),*)*; $($resttt:tt)* )
     ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion_item ($($headtt)*)
                      ($fld) ($fldty) ($fcn) (pub fn) ($($($lt),*),*) ($($args)*) ($(-> $r)*) ($($($aty : $atyb_first $(:: $ayb_first_ext)* $(+ $ayb_rest $(:: $ayb_rest_ext)*)*),*)*)
                      ($fld as $fldty : $($resttt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt as $fldty:ty :
      fn $fcn:ident $(< $($lt:tt),* >)* ($($args:tt)*) $(-> $r:ty)* $(where $($aty:ident : $atyb_first:tt $(:: $ayb_first_ext:tt)* $(+ $ayb_rest:tt $(:: $ayb_rest_ext:tt)*)*),*)*; $($resttt:tt)* )
     ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion_item ($($headtt)*)
                      ($fld) ($fldty) ($fcn) (fn) ($($($lt),*),*) ($($args)*) ($(-> $r)*) ($($($aty : $atyb_first $(:: $ayb_first_ext)* $(+ $ayb_rest $(:: $ayb_rest_ext)*)*),*)*)
                      ($fld as $fldty : $($resttt)*) ($($tailtt)*));};

    (@impl_expansion_item ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) ($($args:tt)*) ($($r:tt)*) ($($b:tt)+)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_bound ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) ($($gp)*) ($($args)*) ($($r)* where $($b)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_item ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) ($($args:tt)*) ($($r:tt)*) ()
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_bound ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) ($($gp)*) ($($args)*) ($($r)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_bound ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)+) ($($args:tt)*) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_gp ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) (<$($gp)*>) ($($args)*) ($($r)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_bound ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) () ($($args:tt)*) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_gp ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) () ($($args)*) ($($r)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) (self) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
                      ($($kwtt)* $fcn $($gp)* (self) $($r)* { (self.$fld).$fcn() })
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) (&self) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
                      ($($kwtt)* $fcn $($gp)* (&self) $($r)* { (self.$fld).$fcn() })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
    ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) (&mut self) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (&mut self) $($r)* { (self.$fld).$fcn() })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   (self, $( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (self,$($a : $at),*) $($r)* { (self.$fld).$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   (&self, $( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (&self,$($a : $at),*) $($r)* { (self.$fld).$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   (&mut self, $( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (&mut self,$($a : $at),*) $($r)* { (self.$fld).$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   ($( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@ impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* ($($a : $at),*) $($r)* { <$fldty>::$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};
}

#[cfg(test)]
mod tests {
    #[derive(Copy, Clone)]
    struct Inner {
        data: usize,
    }

    impl Inner {
        pub fn new_inner() -> Self {
            Inner { data: 42 }
        }

        pub fn new_inner_add(val: usize) -> Self {
            Inner { data: 42 + val }
        }

        pub fn noop(&self) {}

        pub fn str_to_owned<'a>(str: &'a str) -> String {
            str.to_owned()
        }

        pub fn get(&self) -> usize {
            self.data
        }

        pub fn get_add(&self, val: usize) -> usize {
            self.data + val
        }

        pub fn reset(&mut self) -> usize {
            self.data = 0;
            self.data
        }

        pub fn set(&mut self, val: usize) -> usize {
            self.data = val;
            self.data
        }

        pub fn to_data(self) -> usize {
            self.data
        }

        pub fn to_data_add(self, val: usize) -> usize {
            self.data + val
        }
    }

    #[test]
    fn test_delegate() {
        #[derive(Copy, Clone)]
        struct Outer {
            inner: Inner,
        }

        delegate_method! {
			      impl Outer {
                inner as Inner:
                fn new_inner() -> Inner;
                fn new_inner_add(val: usize) -> Inner;
                fn noop(&self);
				        fn str_to_owned<'a>(str: &'a str) -> String;
                fn get(&self) -> usize;
                fn get_add(&self, val: usize) -> usize;
                fn reset(&mut self) -> usize;
                fn set(&mut self, val: usize) -> usize;
                inner as Inner:
                fn to_data(self) -> usize;
                fn to_data_add(self, val: usize) -> usize;
            }
        }

        assert_eq!(Outer::new_inner().get(), 42);
        assert_eq!(Outer::new_inner_add(58).get(), 100);

        assert_eq!(Outer::str_to_owned("Hello"), "Hello".to_owned());

        let mut x = Outer { inner: Inner::new_inner() };

        assert_eq!(x.get(), 42);
        assert_eq!(x.get_add(58), 100);
        x.noop();

        x.reset();
        assert_eq!(x.get(), 0);

        x.set(36);
        assert_eq!(x.get(), 36);

        assert_eq!(x.clone().to_data(), 36);
        assert_eq!(x.clone().to_data_add(4), 40);
    }
}
<commit_msg>Add support for ?Sized bound (only in the first position).<commit_after>#[macro_export]
macro_rules! delegate_method {
    {impl $($tt:tt)*} =>
    {delegate_method!{@impl_collect () $($tt)*}};

    {@impl_collect ($($headtt:tt)*) {$($tailtt:tt)*}} =>
    {delegate_method!{@impl_expansion ($($headtt)*) ($($tailtt)*) ()}};

    {@impl_collect ($($headtt:tt)*) $tt:tt $($tailtt:tt)*} =>
    {delegate_method!{@impl_collect ($($headtt)* $tt) $($tailtt)*}};

    {@impl_expansion_impl ($($headtt:tt)*) ($($itemtt:tt)*) ($($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!{@impl_expansion ($($headtt)*) ($($resttt)*) ($($tailtt)* #[inline] $($itemtt)*)}};

    (@impl_finalization ($($headtt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@as_item #[allow(dead_code)]impl $($headtt)* { $($tailtt)* });};

    (@as_item $i:item) => { $i };

    {@impl_expansion ($($headtt:tt)*) () ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_finalization ($($headtt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     (type $ascty:ident = $dstty:ty; $($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*) ($($resttt)*) ($($tailtt)* type $ascty = $dstty; ));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt $(as $fldty:ty)* : ) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*) () ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld_last:tt $(as $fldty_last:ty)* :
      $fld:tt $(as $fldty:ty)* : $($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*)
                      ($fld $(as $fldty)* : $($resttt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt : $($resttt:tt)*) ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion ($($headtt)*) ($fld as () : $($resttt)*) ($($tailtt)*))};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt as $fldty:ty :
      pub fn $fcn:ident $(< $($lt:tt),* >)* ($($args:tt)*) $(-> $r:ty)* $(where $($aty:ident : ?Sized $(+ $ayb_rest:tt $(:: $ayb_rest_ext:tt)*)*),*)*; $($resttt:tt)* )
     ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion_item ($($headtt)*)
                      ($fld) ($fldty) ($fcn) (pub fn) ($($($lt),*),*) ($($args)*) ($(-> $r)*) ($($($aty : ?Sized $(+ $ayb_rest $(:: $ayb_rest_ext)*)*),*)*)
                      ($fld as $fldty : $($resttt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt as $fldty:ty :
      fn $fcn:ident $(< $($lt:tt),* >)* ($($args:tt)*) $(-> $r:ty)* $(where $($aty:ident : ?Sized $(+ $ayb_rest:tt $(:: $ayb_rest_ext:tt)*)*),*)*; $($resttt:tt)* )
     ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion_item ($($headtt)*)
                      ($fld) ($fldty) ($fcn) (fn) ($($($lt),*),*) ($($args)*) ($(-> $r)*) ($($($aty : ?Sized $(+ $ayb_rest $(:: $ayb_rest_ext)*)*),*)*)
                      ($fld as $fldty : $($resttt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt as $fldty:ty :
      pub fn $fcn:ident $(< $($lt:tt),* >)* ($($args:tt)*) $(-> $r:ty)* $(where $($aty:ident : $atyb_first:tt $(:: $ayb_first_ext:tt)* $(+ $ayb_rest:tt $(:: $ayb_rest_ext:tt)*)*),*)*; $($resttt:tt)* )
     ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion_item ($($headtt)*)
                      ($fld) ($fldty) ($fcn) (pub fn) ($($($lt),*),*) ($($args)*) ($(-> $r)*) ($($($aty : $atyb_first $(:: $ayb_first_ext)* $(+ $ayb_rest $(:: $ayb_rest_ext)*)*),*)*)
                      ($fld as $fldty : $($resttt)*) ($($tailtt)*));};

    {@impl_expansion ($($headtt:tt)*)
     ($fld:tt as $fldty:ty :
      fn $fcn:ident $(< $($lt:tt),* >)* ($($args:tt)*) $(-> $r:ty)* $(where $($aty:ident : $atyb_first:tt $(:: $ayb_first_ext:tt)* $(+ $ayb_rest:tt $(:: $ayb_rest_ext:tt)*)*),*)*; $($resttt:tt)* )
     ($($tailtt:tt)*)} =>
    {delegate_method!(@impl_expansion_item ($($headtt)*)
                      ($fld) ($fldty) ($fcn) (fn) ($($($lt),*),*) ($($args)*) ($(-> $r)*) ($($($aty : $atyb_first $(:: $ayb_first_ext)* $(+ $ayb_rest $(:: $ayb_rest_ext)*)*),*)*)
                      ($fld as $fldty : $($resttt)*) ($($tailtt)*));};

    (@impl_expansion_item ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) ($($args:tt)*) ($($r:tt)*) ($($b:tt)+)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_bound ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) ($($gp)*) ($($args)*) ($($r)* where $($b)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_item ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) ($($args:tt)*) ($($r:tt)*) ()
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_bound ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) ($($gp)*) ($($args)*) ($($r)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_bound ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)+) ($($args:tt)*) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_gp ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) (<$($gp)*>) ($($args)*) ($($r)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_bound ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) () ($($args:tt)*) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_gp ($($headtt)*)
                      ($fld) ($fldty) ($fcn) ($($kwtt)*) () ($($args)*) ($($r)*)
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) (self) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
                      ($($kwtt)* $fcn $($gp)* (self) $($r)* { (self.$fld).$fcn() })
                      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
     ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) (&self) ($($r:tt)*)
     ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
                      ($($kwtt)* $fcn $($gp)* (&self) $($r)* { (self.$fld).$fcn() })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
    ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*) (&mut self) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (&mut self) $($r)* { (self.$fld).$fcn() })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   (self, $( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (self,$($a : $at),*) $($r)* { (self.$fld).$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   (&self, $( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (&self,$($a : $at),*) $($r)* { (self.$fld).$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   (&mut self, $( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* (&mut self,$($a : $at),*) $($r)* { (self.$fld).$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};

    (@impl_expansion_gp ($($headtt:tt)*)
   ($fld:tt) ($fldty:ty) ($fcn:ident) ($($kwtt:tt)*) ($($gp:tt)*)
   ($( $a:ident : $at:ty ),*) ($($r:tt)*)
    ($($resttt:tt)*) ($($tailtt:tt)*)) =>
    {delegate_method!(@ impl_expansion_impl ($($headtt)*)
      ($($kwtt)* $fcn $($gp)* ($($a : $at),*) $($r)* { <$fldty>::$fcn($($a),*) })
      ($($resttt)*) ($($tailtt)*));};
}

#[cfg(test)]
mod tests {
    #[derive(Copy, Clone)]
    struct Inner {
        data: usize,
    }

    impl Inner {
        pub fn new_inner() -> Self {
            Inner { data: 42 }
        }

        pub fn new_inner_add(val: usize) -> Self {
            Inner { data: 42 + val }
        }

        pub fn noop(&self) {}

        pub fn str_to_owned<'a>(str: &'a str) -> String {
            str.to_owned()
        }

        pub fn get(&self) -> usize {
            self.data
        }

        pub fn get_add(&self, val: usize) -> usize {
            self.data + val
        }

        pub fn reset(&mut self) -> usize {
            self.data = 0;
            self.data
        }

        pub fn set(&mut self, val: usize) -> usize {
            self.data = val;
            self.data
        }

        pub fn to_data(self) -> usize {
            self.data
        }

        pub fn to_data_add(self, val: usize) -> usize {
            self.data + val
        }

        pub fn to_data_ignore_unsized<R>(self, _: Box<R>) -> usize where R: ?Sized + Clone {
            self.data
        }
    }

    #[test]
    fn test_delegate() {
        #[derive(Copy, Clone)]
        struct Outer {
            inner: Inner,
        }

        delegate_method! {
			impl Outer {
                inner as Inner:
                fn new_inner() -> Inner;
                fn new_inner_add(val: usize) -> Inner;
                fn noop(&self);
				fn str_to_owned<'a>(str: &'a str) -> String;
                fn get(&self) -> usize;
                fn get_add(&self, val: usize) -> usize;
                fn reset(&mut self) -> usize;
                fn set(&mut self, val: usize) -> usize;
                inner as Inner:
                fn to_data(self) -> usize;
                fn to_data_add(self, val: usize) -> usize;

                fn to_data_ignore_unsized<R>(self, r: Box<R>) -> usize where R: ?Sized + Clone;
            }
        }

        assert_eq!(Outer::new_inner().get(), 42);
        assert_eq!(Outer::new_inner_add(58).get(), 100);

        assert_eq!(Outer::str_to_owned("Hello"), "Hello".to_owned());

        let mut x = Outer { inner: Inner::new_inner() };

        assert_eq!(x.get(), 42);
        assert_eq!(x.get_add(58), 100);
        x.noop();

        x.reset();
        assert_eq!(x.get(), 0);

        x.set(36);
        assert_eq!(x.get(), 36);

        assert_eq!(x.clone().to_data(), 36);
        assert_eq!(x.clone().to_data_add(4), 40);

        assert_eq!(x.clone().to_data_ignore_unsized(Box::new(3)), 36);
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Martin Grabmueller. See the LICENSE file at the
// top-level directory of this distribution for license information.
//

#[macro_use]
extern crate log;
extern crate uuid;
extern crate getopts;
extern crate openssl;

use uuid::Uuid;
use getopts::Options;
use openssl::ssl;
use openssl::x509;

use std::io::Write;
use std::io::Read;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;

pub mod error;

/// Print a usage summary to stdout that describes the command syntax.
pub fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

pub struct NodeState {
    pub ssl_context: ssl::SslContext
}

impl NodeState {
    pub fn new(config: &Config) -> Result<NodeState, error::Error> {
        let mut ssl_context = try!(ssl::SslContext::new(ssl::SslMethod::Tlsv1));

        let mut ca_path = config.workspace_dir.clone();
        ca_path.push_str("/ca-chain.cert.pem");

        trace!("setting ca file");
        try!(ssl_context.set_CA_file(&ca_path));

        let mut cert_path = config.workspace_dir.clone();
        cert_path.push_str("/");
        cert_path.push_str(&config.uuid.to_string());
        cert_path.push_str(".cert.pem");

        trace!("setting certificate file");
        try!(ssl_context.set_certificate_file(&cert_path, x509::X509FileType::PEM));

        let mut priv_key_path = config.workspace_dir.clone();
        priv_key_path.push_str("/");
        priv_key_path.push_str(&config.uuid.to_string());
        priv_key_path.push_str(".key.pem");

        trace!("setting private key file");
        try!(ssl_context.set_private_key_file(&priv_key_path,
                                              x509::X509FileType::PEM));
        try!(ssl_context.check_private_key());

        Ok(NodeState {
            ssl_context: ssl_context
        })
    }
}

/// Configuration of a peerington node.
pub struct Config {
    /// UUID of the node.
    pub uuid: Uuid,
    /// The addresses this node will attempt to listen on.
    pub listen_addresses: Vec<String>,
    /// The addresses this node will use to connect to a cluster of
    /// peerington nodes.
    pub seed_addresses: Vec<String>,
    /// Local storage space where some data is stored during
    /// operation.
    pub workspace_dir: String
}

/// Parse allowed options for a peerington node.  Either return a
/// complete `Config' value or an error value.
pub fn parse_opts(args: Vec<String>) -> Result<Config, error::ConfigError> {

    let mut opts = Options::new();
    opts.optopt("u", "uuid", "set node uuid", "UUID");
    opts.optopt("w", "workspace", "set workspace directory", "DIRECTORY");
    opts.optmulti("l", "listen", "set listen address(es)", "ADDRESS:PORT");
    opts.optmulti("s", "seed", "set seed address(es)", "ADDRESS:PORT");
    opts.optflag("h", "help", "print this help menu");

    match opts.parse(&args[1..]) {
        Ok(matches) => {
            if matches.opt_present("h") {
                return Err(error::ConfigError::HelpRequested(opts));
            }

            let addresses = if matches.opt_present("l") {
                matches.opt_strs("l")
            } else {
                return Err(error::ConfigError::NoListenAddress);
            };

            let seeds = matches.opt_strs("s");

            let workspace =
                match matches.opt_str("w") {
                    Some(w) => w,
                    None => {
                        return Err(error::ConfigError::NoWorkspace);
                    }
                };

            let uuid =
                match matches.opt_str("u") {
                    Some(uuid) => {
                        match Uuid::parse_str(uuid.chars().as_str()) {
                            Ok(u) => {
                                u
                            }
                            Err(e) => {
                                return Err(error::ConfigError::InvalidUuid(e));
                            }
                        }
                    },
                    None => {
                        return Err(error::ConfigError::NoUuid);
                    }
                };

            return Ok(Config {
                listen_addresses: addresses,
                seed_addresses: seeds,
                workspace_dir: workspace,
                uuid: uuid
            });
        }
        Err(f) => {
            return Err(error::ConfigError::GetOptError(f));
        }
    };
}

fn handler(stream: &mut ssl::SslStream<TcpStream>) {
    if let Ok(peer) = stream.get_ref().peer_addr() {
        info!("connection from {}", peer);
    } else {
        info!("connection attempt");
    }
    let mut buf = [33; 1024];
    loop {
        match stream.ssl_read(&mut buf) {
            Ok(0) =>
                break,
            Ok(n) => {
                match stream.ssl_write(&buf[0..n]) {
                    Ok(x) =>
                        if x != n {
                            error!("could not write everything");
                            break;
                        },
                    Err(e) => {
                        error!("error when writing: {}", e);
                    }
                }
            },
            Err(e) => {
                error!("error when reading: {}", e);
                break;
            }
        }
    }
    if let Ok(peer) = stream.get_ref().peer_addr() {
        info!("closed connection from {}", peer);
    } else {
        info!("closed connection");
    }
}

fn listener(addr: String, ssl_context: &ssl::SslContext) {
    match TcpListener::bind(addr.as_str()) {
        Ok(listener) => {
            info!("listening on {}", listener.local_addr().unwrap());
            for stream in listener.incoming() {
                match stream {
                    Ok(mut stream) => {
                        match ssl::Ssl::new(ssl_context) {
                            Ok(ssl) => {
                                thread::spawn(move || {
                                    match ssl::SslStream::connect(ssl, stream) {
                                        Ok(mut ssl_stream) => {
                                            handler(&mut ssl_stream);
                                        },
                                        Err(e) => {
                                            error!("error when accepting connection: {}",
                                                   e)
                                        }
                                    }
                                });
                                ()
                            },
                            Err(e) => {
                                error!("error when accepting connection to {}: {}",
                                       addr, e)
                            }
                        }
                    },
                    Err(e) =>
                        error!("error when accepting connection to {}: {}",
                                 addr, e)
                }
            }
        },
        Err(e) => {
            error!("error when binding to {}: {}", addr, e);
        }
    }
}

pub fn start_listeners(config: &Config, ssl_context: &ssl::SslContext) {
    for addr in config.listen_addresses.clone() {
        let a = addr.clone();
        let ssl_ctx = ssl_context.clone();
        thread::spawn(move || listener(a, &ssl_ctx));
    }
}

mod test {
    #[allow(unused_imports)]
    use super::*;

    #[test]
    fn no_listen() {
        let ok = match parse_opts(vec!["prog".to_string()]) {
            Err(ConfigError::NoListenAddress) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_workspace() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string()]) {
            Err(ConfigError::NoWorkspace) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string()]) {
            Err(ConfigError::NoUuid) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string(),
                                       "--uuid=abcde".to_string()]) {
            Err(ConfigError::InvalidUuid(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--hepl".to_string()]) {
            Err(ConfigError::GetOptError(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "-h".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn long_help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--help".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn all_fine() {
        match parse_opts(vec!["prog".to_string(),
                              "--listen=localhost:1234".to_string(),
                              "--workspace=space".to_string(),
                              "--uuid=33d92212-7bf3-4573-8052-17789f520240".to_string()]) {
            Ok(config) => {
                assert!(config.listen_addresses.len() >= 1);
                for a in config.listen_addresses {
                    assert!(a.len() > 0);
                }
                assert!(config.workspace_dir.len() >= 1);
            }
            _ => {
                assert!(false);
            }
        };
    }

}
<commit_msg>Properly accept TLS connection, support client cert verification.<commit_after>// Copyright 2016 Martin Grabmueller. See the LICENSE file at the
// top-level directory of this distribution for license information.
//

#[macro_use]
extern crate log;
extern crate uuid;
extern crate getopts;
extern crate openssl;

use uuid::Uuid;
use getopts::Options;
use openssl::ssl;
use openssl::x509;
use openssl::x509::X509ValidationError::*;

use std::io::Read;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;

pub mod error;

/// Print a usage summary to stdout that describes the command syntax.
pub fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

pub struct NodeState {
    pub ssl_context: ssl::SslContext
}

fn verify_client_cert(preverify_ok: bool, x509_ctx: &x509::X509StoreContext) -> bool {
    if preverify_ok {
         return true;
    } else {
        let err =
            match x509_ctx.get_error() {
                Some(e) =>
                    match e {
                        X509UnableToGetIssuerCert => "X509UnableToGetIssuerCert",
                        X509UnableToGetCrl => "X509UnableToGetCrl",
                        X509UnableToDecryptCertSignature => "X509UnableToDecryptCertSignature",
                        X509UnableToDecryptCrlSignature => "X509UnableToDecryptCrlSignature",
                        X509UnableToDecodeIssuerPublicKey => "X509UnableToDecodeIssuerPublicKey",
                        X509CertSignatureFailure => "X509CertSignatureFailure",
                        X509CrlSignatureFailure => "X509CrlSignatureFailure",
                        X509CertNotYetValid => "X509CertNotYetValid",
                        X509CertHasExpired => "X509CertHasExpired",
                        X509CrlNotYetValid => "X509CrlNotYetValid",
                        X509CrlHasExpired => "X509CrlHasExpired",
                        X509ErrorInCertNotBeforeField => "X509ErrorInCertNotBeforeField",
                        X509ErrorInCertNotAfterField => "X509ErrorInCertNotAfterField",
                        X509ErrorInCrlLastUpdateField => "X509ErrorInCrlLastUpdateField",
                        X509ErrorInCrlNextUpdateField => "X509ErrorInCrlNextUpdateField",
                        X509OutOfMem => "X509OutOfMem",
                        X509DepthZeroSelfSignedCert => "X509DepthZeroSelfSignedCert",
                        X509SelfSignedCertInChain => "X509SelfSignedCertInChain",
                        X509UnableToGetIssuerCertLocally => "X509UnableToGetIssuerCertLocally",
                        X509UnableToVerifyLeafSignature => "X509UnableToVerifyLeafSignature",
                        X509CertChainTooLong => "X509CertChainTooLong",
                        X509CertRevoked => "X509CertRevoked",
                        X509InvalidCA => "X509InvalidCA",
                        X509PathLengthExceeded => "X509PathLengthExceeded",
                        X509InvalidPurpose => "X509InvalidPurpose",
                        X509CertUntrusted => "X509CertUntrusted",
                        X509CertRejected => "X509CertRejected",
                        X509SubjectIssuerMismatch => "X509SubjectIssuerMismatch",
                        X509AkidSkidMismatch => "X509AkidSkidMismatch",
                        X509AkidIssuerSerialMismatch => "X509AkidIssuerSerialMismatch",
                        X509KeyusageNoCertsign => "X509KeyusageNoCertsign",
                        X509UnableToGetCrlIssuer => "X509UnableToGetCrlIssuer",
                        X509UnhandledCriticalExtension => "X509UnhandledCriticalExtension",
                        X509KeyusageNoCrlSign => "X509KeyusageNoCrlSign",
                        X509UnhandledCriticalCrlExtension => "X509UnhandledCriticalCrlExtension",
                        X509InvalidNonCA => "X509InvalidNonCA",
                        X509ProxyPathLengthExceeded => "X509ProxyPathLengthExceeded",
                        X509KeyusageNoDigitalSignature => "X509KeyusageNoDigitalSignature",
                        X509ProxyCertificatesNotAllowed => "X509ProxyCertificatesNotAllowed",
                        X509InvalidExtension => "X509InvalidExtension",
                        X509InavlidPolicyExtension => "X509InavlidPolicyExtension",
                        X509NoExplicitPolicy => "X509NoExplicitPolicy",
                        X509DifferentCrlScope => "X509DifferentCrlScope",
                        X509UnsupportedExtensionFeature => "X509UnsupportedExtensionFeature",
                        X509UnnestedResource => "X509UnnestedResource",
                        X509PermittedVolation => "X509PermittedVolation",
                        X509ExcludedViolation => "X509ExcludedViolation",
                        X509SubtreeMinmax => "X509SubtreeMinmax",
                        X509UnsupportedConstraintType => "X509UnsupportedConstraintType",
                        X509UnsupportedConstraintSyntax => "X509UnsupportedConstraintSyntax",
                        X509UnsupportedNameSyntax => "X509UnsupportedNameSyntax",
                        X509CrlPathValidationError => "X509CrlPathValidationError",
                        X509ApplicationVerification => "X509ApplicationVerification",
                        X509UnknownError(_i) => "X509UnknownError"
                    },
                None => "no error"
            };

        error!("client certificate check failed at depth {}: {}", x509_ctx.error_depth(), err);

        return false;
    }
}

impl NodeState {
    pub fn new(config: &Config) -> Result<NodeState, error::Error> {
        let mut ssl_context = try!(ssl::SslContext::new(ssl::SslMethod::Tlsv1));

        let mut ca_path = config.workspace_dir.clone();
        ca_path.push_str("/ca-chain.cert.pem");

        trace!("setting ca file");
        try!(ssl_context.set_CA_file(&ca_path));

        let mut cert_path = config.workspace_dir.clone();
        cert_path.push_str("/");
        cert_path.push_str(&config.uuid.to_string());
        cert_path.push_str(".cert.pem");

        trace!("setting certificate file");
        try!(ssl_context.set_certificate_file(&cert_path, x509::X509FileType::PEM));

        let mut priv_key_path = config.workspace_dir.clone();
        priv_key_path.push_str("/");
        priv_key_path.push_str(&config.uuid.to_string());
        priv_key_path.push_str(".key.pem");

        trace!("setting private key file");
        try!(ssl_context.set_private_key_file(&priv_key_path,
                                              x509::X509FileType::PEM));
        try!(ssl_context.check_private_key());

        ssl_context.set_verify(ssl::SSL_VERIFY_PEER | ssl::SSL_VERIFY_FAIL_IF_NO_PEER_CERT, Some(verify_client_cert));

        Ok(NodeState {
            ssl_context: ssl_context
        })
    }
}

/// Configuration of a peerington node.
pub struct Config {
    /// UUID of the node.
    pub uuid: Uuid,
    /// The addresses this node will attempt to listen on.
    pub listen_addresses: Vec<String>,
    /// The addresses this node will use to connect to a cluster of
    /// peerington nodes.
    pub seed_addresses: Vec<String>,
    /// Local storage space where some data is stored during
    /// operation.
    pub workspace_dir: String
}

/// Parse allowed options for a peerington node.  Either return a
/// complete `Config' value or an error value.
pub fn parse_opts(args: Vec<String>) -> Result<Config, error::ConfigError> {

    let mut opts = Options::new();
    opts.optopt("u", "uuid", "set node uuid", "UUID");
    opts.optopt("w", "workspace", "set workspace directory", "DIRECTORY");
    opts.optmulti("l", "listen", "set listen address(es)", "ADDRESS:PORT");
    opts.optmulti("s", "seed", "set seed address(es)", "ADDRESS:PORT");
    opts.optflag("h", "help", "print this help menu");

    match opts.parse(&args[1..]) {
        Ok(matches) => {
            if matches.opt_present("h") {
                return Err(error::ConfigError::HelpRequested(opts));
            }

            let addresses = if matches.opt_present("l") {
                matches.opt_strs("l")
            } else {
                return Err(error::ConfigError::NoListenAddress);
            };

            let seeds = matches.opt_strs("s");

            let workspace =
                match matches.opt_str("w") {
                    Some(w) => w,
                    None => {
                        return Err(error::ConfigError::NoWorkspace);
                    }
                };

            let uuid =
                match matches.opt_str("u") {
                    Some(uuid) => {
                        match Uuid::parse_str(uuid.chars().as_str()) {
                            Ok(u) => {
                                u
                            }
                            Err(e) => {
                                return Err(error::ConfigError::InvalidUuid(e));
                            }
                        }
                    },
                    None => {
                        return Err(error::ConfigError::NoUuid);
                    }
                };

            return Ok(Config {
                listen_addresses: addresses,
                seed_addresses: seeds,
                workspace_dir: workspace,
                uuid: uuid
            });
        }
        Err(f) => {
            return Err(error::ConfigError::GetOptError(f));
        }
    };
}

fn handler(stream: &mut ssl::SslStream<TcpStream>) {
    if let Ok(peer) = stream.get_ref().peer_addr() {
        info!("connection from {}", peer);
    } else {
        info!("connection attempt");
    }
    let mut buf = [33; 1024];
    loop {
        match stream.ssl_read(&mut buf) {
            Ok(0) =>
                break,
            Ok(n) => {
                match stream.ssl_write(&buf[0..n]) {
                    Ok(x) =>
                        if x != n {
                            error!("could not write everything");
                            break;
                        },
                    Err(e) => {
                        error!("error when writing: {}", e);
                    }
                }
            },
            Err(e) => {
                error!("error when reading: {}", e);
                break;
            }
        }
    }
    if let Ok(peer) = stream.get_ref().peer_addr() {
        info!("closed connection from {}", peer);
    } else {
        info!("closed connection");
    }
}

fn listener(addr: String, ssl_context: &ssl::SslContext) {
    match TcpListener::bind(addr.as_str()) {
        Ok(listener) => {
            info!("listening on {}", listener.local_addr().unwrap());
            for stream in listener.incoming() {
                match stream {
                    Ok(stream) => {
                        match ssl::Ssl::new(ssl_context) {
                            Ok(ssl) => {
                                thread::spawn(move || {
                                    match ssl::SslStream::accept(ssl, stream) {
                                        Ok(mut ssl_stream) => {
                                            handler(&mut ssl_stream);
                                        },
                                        Err(e) => {
                                            error!("error when accepting connection: {}",
                                                   e)
                                        }
                                    }
                                });
                                ()
                            },
                            Err(e) => {
                                error!("error when accepting connection to {}: {}",
                                       addr, e)
                            }
                        }
                    },
                    Err(e) =>
                        error!("error when accepting connection to {}: {}",
                                 addr, e)
                }
            }
        },
        Err(e) => {
            error!("error when binding to {}: {}", addr, e);
        }
    }
}

pub fn start_listeners(config: &Config, ssl_context: &ssl::SslContext) {
    for addr in config.listen_addresses.clone() {
        let a = addr.clone();
        let ssl_ctx = ssl_context.clone();
        thread::spawn(move || listener(a, &ssl_ctx));
    }
}

mod test {
    #[allow(unused_imports)]
    use super::*;

    #[test]
    fn no_listen() {
        let ok = match parse_opts(vec!["prog".to_string()]) {
            Err(ConfigError::NoListenAddress) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_workspace() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string()]) {
            Err(ConfigError::NoWorkspace) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn no_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string()]) {
            Err(ConfigError::NoUuid) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_uuid() {
        let ok = match parse_opts(vec!["prog".to_string(),
                                       "--listen=localhost:1234".to_string(),
                                       "--workspace=space".to_string(),
                                       "--uuid=abcde".to_string()]) {
            Err(ConfigError::InvalidUuid(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn invalid_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--hepl".to_string()]) {
            Err(ConfigError::GetOptError(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "-h".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn long_help_arg() {
        let ok = match parse_opts(vec!["prog".to_string(), "--help".to_string()]) {
            Err(ConfigError::HelpRequested(_)) => true,
            _ => false
        };
        assert!(ok);
    }

    #[test]
    fn all_fine() {
        match parse_opts(vec!["prog".to_string(),
                              "--listen=localhost:1234".to_string(),
                              "--workspace=space".to_string(),
                              "--uuid=33d92212-7bf3-4573-8052-17789f520240".to_string()]) {
            Ok(config) => {
                assert!(config.listen_addresses.len() >= 1);
                for a in config.listen_addresses {
                    assert!(a.len() > 0);
                }
                assert!(config.workspace_dir.len() >= 1);
            }
            _ => {
                assert!(false);
            }
        };
    }

}
<|endoftext|>


<commit_before>//! Struct and methods for union-find operation.

#![warn(bad_style, missing_docs,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

#![cfg_attr(all(test, feature = "nightly"), feature(test))]

use std::default::Default;
use std::iter::FromIterator;

/// The value that can be contained with `UfValue`.
pub trait UfValue: Sized + Default {
    /// Merge two value into one.
    ///
    /// This is used by `UnionFind::union` operation.
    fn merge(lval: Self, rval: Self) -> Merge<Self>;
}

/// Return value of the `UfValue::merege`
#[allow(missing_docs)]
pub enum Merge<T> {
    Left(T), Right(T)
}

/// Reperesents the size of the group.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Size(usize);

impl UfValue for Size {
    fn merge(Size(lval): Size, Size(rval): Size) -> Merge<Size> {
        if lval >= rval {
            Merge::Left(Size(lval + rval))
        } else {
            Merge::Right(Size(lval + rval))
        }
    }
}

impl Default for Size {
    fn default() -> Size { Size(1) }
}

/// APIs for Union-Find operation.
pub trait UnionFind<V: UfValue>: FromIterator<V> + Sized {
    /// Creates empty `UnionFind` struct.
    #[inline]
    fn new(len: usize) -> Self where V: Default {
        Self::from_iter((0..len).map(|_| Default::default()))
    }

    /// Returns the size of `self`.
    fn size(&self) -> usize;

    /// Join two sets that contains given keys (Union operation).
    ///
    /// Returns `true` if these keys are belonged to different sets.
    fn union(&mut self, key0: usize, key1: usize) -> bool;

    /// Returns the identifier of the set that the key belongs to.
    fn find(&mut self, key: usize) -> usize;

    /// Returns the reference to the value of the set that the key belongs to.
    fn get(&mut self, key: usize) -> &V;

    /// Returns the mutable reference to the value of the set that the key belongs to.
    fn get_mut(&mut self, key: usize) -> &mut V;
}

mod quick_union;
pub use quick_union::QuickUnionUf;

#[cfg(test)]
mod tests;

#[cfg(all(test, feature = "nightly"))]
mod bench;
<commit_msg>More tight lint options.<commit_after>//! Struct and methods for union-find operation.

#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unused_extern_crates)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
#![warn(unused_results)]

#![cfg_attr(all(test, feature = "nightly"), feature(test))]

use std::default::Default;
use std::iter::FromIterator;

/// The value that can be contained with `UfValue`.
pub trait UfValue: Sized + Default {
    /// Merge two value into one.
    ///
    /// This is used by `UnionFind::union` operation.
    fn merge(lval: Self, rval: Self) -> Merge<Self>;
}

/// Return value of the `UfValue::merege`
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug)]
pub enum Merge<T> {
    Left(T), Right(T)
}

/// Reperesents the size of the group.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Size(usize);

impl UfValue for Size {
    fn merge(Size(lval): Size, Size(rval): Size) -> Merge<Size> {
        if lval >= rval {
            Merge::Left(Size(lval + rval))
        } else {
            Merge::Right(Size(lval + rval))
        }
    }
}

impl Default for Size {
    fn default() -> Size { Size(1) }
}

/// APIs for Union-Find operation.
pub trait UnionFind<V: UfValue>: FromIterator<V> + Sized {
    /// Creates empty `UnionFind` struct.
    #[inline]
    fn new(len: usize) -> Self where V: Default {
        Self::from_iter((0..len).map(|_| Default::default()))
    }

    /// Returns the size of `self`.
    fn size(&self) -> usize;

    /// Join two sets that contains given keys (Union operation).
    ///
    /// Returns `true` if these keys are belonged to different sets.
    fn union(&mut self, key0: usize, key1: usize) -> bool;

    /// Returns the identifier of the set that the key belongs to.
    fn find(&mut self, key: usize) -> usize;

    /// Returns the reference to the value of the set that the key belongs to.
    fn get(&mut self, key: usize) -> &V;

    /// Returns the mutable reference to the value of the set that the key belongs to.
    fn get_mut(&mut self, key: usize) -> &mut V;
}

mod quick_union;
pub use quick_union::QuickUnionUf;

#[cfg(test)]
mod tests;

#[cfg(all(test, feature = "nightly"))]
mod bench;
<|endoftext|>