<commit_before>//! Global machine state as well as implementation of the interpreter engine
//! `Machine` trait.

use std::rc::Rc;
use std::borrow::Cow;
use std::collections::HashMap;
use std::cell::RefCell;

use rand::rngs::StdRng;

use syntax::attr;
use syntax::symbol::sym;
use rustc::hir::def_id::DefId;
use rustc::ty::{self, layout::{Size, LayoutOf}, query::TyCtxtAt};
use rustc::mir;

use crate::*;

// Some global facts about the emulated machine.
pub const PAGE_SIZE: u64 = 4*1024; // FIXME: adjust to target architecture
pub const STACK_ADDR: u64 = 32*PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
pub const STACK_SIZE: u64 = 16*PAGE_SIZE; // whatever
pub const NUM_CPUS: u64 = 1;

/// Extra memory kinds
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MiriMemoryKind {
    /// `__rust_alloc` memory.
    Rust,
    /// `malloc` memory.
    C,
    /// Windows `HeapAlloc` memory.
    WinHeap,
    /// Part of env var emulation.
    Env,
    /// Statics.
    Static,
}

impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
    #[inline(always)]
    fn into(self) -> MemoryKind<MiriMemoryKind> {
        MemoryKind::Machine(self)
    }
}

/// Extra per-allocation data
#[derive(Debug, Clone)]
pub struct AllocExtra {
    /// Stacked Borrows state is only added if validation is enabled.
    pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
}

/// Extra global memory data
#[derive(Clone, Debug)]
pub struct MemoryExtra {
    pub stacked_borrows: stacked_borrows::MemoryExtra,
    pub intptrcast: intptrcast::MemoryExtra,

    /// The random number generator to use if Miri is running in non-deterministic mode and to
    /// enable intptrcast
    pub(crate) rng: Option<RefCell<StdRng>>,

    /// Whether to enforce the validity invariant.
    pub(crate) validate: bool,
}

impl MemoryExtra {
    pub fn new(rng: Option<StdRng>, validate: bool) -> Self {
        MemoryExtra {
            stacked_borrows: Default::default(),
            intptrcast: Default::default(),
            rng: rng.map(RefCell::new),
            validate,
        }
    }
}

/// The machine itself.
pub struct Evaluator<'tcx> {
    /// Environment variables set by `setenv`.
    /// Miri does not expose env vars from the host to the emulated program.
    pub(crate) env_vars: HashMap<Vec<u8>, Pointer<Tag>>,

    /// Program arguments (`Option` because we can only initialize them after creating the ecx).
    /// These are *pointers* to argc/argv because macOS.
    /// We also need the full command line as one string because of Windows.
    pub(crate) argc: Option<Pointer<Tag>>,
    pub(crate) argv: Option<Pointer<Tag>>,
    pub(crate) cmd_line: Option<Pointer<Tag>>,

    /// Last OS error.
    pub(crate) last_error: u32,

    /// TLS state.
    pub(crate) tls: TlsData<'tcx>,
}

impl<'tcx> Evaluator<'tcx> {
    pub(crate) fn new() -> Self {
        Evaluator {
            env_vars: HashMap::default(),
            argc: None,
            argv: None,
            cmd_line: None,
            last_error: 0,
            tls: TlsData::default(),
        }
    }
}

/// A rustc InterpCx for Miri.
pub type MiriEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, Evaluator<'tcx>>;

/// A little trait that's useful to be inherited by extension traits.
pub trait MiriEvalContextExt<'mir, 'tcx> {
    fn eval_context_ref(&self) -> &MiriEvalContext<'mir, 'tcx>;
    fn eval_context_mut(&mut self) -> &mut MiriEvalContext<'mir, 'tcx>;
}
impl<'mir, 'tcx> MiriEvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx> {
    #[inline(always)]
    fn eval_context_ref(&self) -> &MiriEvalContext<'mir, 'tcx> {
        self
    }
    #[inline(always)]
    fn eval_context_mut(&mut self) -> &mut MiriEvalContext<'mir, 'tcx> {
        self
    }
}

/// Machine hook implementations.
impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
    type MemoryKinds = MiriMemoryKind;

    type FrameExtra = stacked_borrows::CallId;
    type MemoryExtra = MemoryExtra;
    type AllocExtra = AllocExtra;
    type PointerTag = Tag;
    type ExtraFnVal = Dlsym;

    type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Tag, Self::AllocExtra>)>;

    const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Static);

    #[inline(always)]
    fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
        ecx.memory().extra.validate
    }

    #[inline(always)]
    fn find_fn(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
        ecx.find_fn(instance, args, dest, ret)
    }

    #[inline(always)]
    fn call_extra_fn(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        fn_val: Dlsym,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> InterpResult<'tcx> {
        ecx.call_dlsym(fn_val, args, dest, ret)
    }

    #[inline(always)]
    fn call_intrinsic(
        ecx: &mut rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
    ) -> InterpResult<'tcx> {
        ecx.call_intrinsic(instance, args, dest)
    }

    #[inline(always)]
    fn ptr_op(
        ecx: &rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
        bin_op: mir::BinOp,
        left: ImmTy<'tcx, Tag>,
        right: ImmTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, (Scalar<Tag>, bool)> {
        ecx.ptr_op(bin_op, left, right)
    }

    fn box_alloc(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        dest: PlaceTy<'tcx, Tag>,
    ) -> InterpResult<'tcx> {
        trace!("box_alloc for {:?}", dest.layout.ty);
        // Call the `exchange_malloc` lang item.
        let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
        let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
        let malloc_mir = ecx.load_mir(malloc.def)?;
        ecx.push_stack_frame(
            malloc,
            malloc_mir.span,
            malloc_mir,
            Some(dest),
            // Don't do anything when we are done. The `statement()` function will increment
            // the old stack frame's stmt counter to the next statement, which means that when
            // `exchange_malloc` returns, we go on evaluating exactly where we want to be.
            StackPopCleanup::None { cleanup: true },
        )?;

        let mut args = ecx.frame().body.args_iter();
        let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;

        // First argument: `size`.
        // (`0` is allowed here -- this is expected to be handled by the lang item).
        let arg = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
        let size = layout.size.bytes();
        ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;

        // Second argument: `align`.
        let arg = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
        let align = layout.align.abi.bytes();
        ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;

        // No more arguments.
        assert!(
            args.next().is_none(),
            "`exchange_malloc` lang item has more arguments than expected"
        );
        Ok(())
    }

    fn find_foreign_static(
        def_id: DefId,
        tcx: TyCtxtAt<'tcx>,
    ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> {
        let attrs = tcx.get_attrs(def_id);
        let link_name = match attr::first_attr_value_str_by_name(&attrs, sym::link_name) {
            Some(name) => name.as_str(),
            None => tcx.item_name(def_id).as_str(),
        };

        let alloc = match link_name.get() {
            "__cxa_thread_atexit_impl" => {
                // This should be all-zero, pointer-sized.
                let size = tcx.data_layout.pointer_size;
                let data = vec![0; size.bytes() as usize];
                Allocation::from_bytes(&data, tcx.data_layout.pointer_align.abi)
            }
            _ => return err!(Unimplemented(
                    format!("can't access foreign static: {}", link_name),
                )),
        };
        Ok(Cow::Owned(alloc))
    }

    #[inline(always)]
    fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>
    {
        // We are not interested in detecting loops.
        Ok(())
    }

    fn tag_allocation<'b>(
        id: AllocId,
        alloc: Cow<'b, Allocation>,
        kind: Option<MemoryKind<Self::MemoryKinds>>,
        memory: &Memory<'mir, 'tcx, Self>,
    ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
        let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
        let alloc = alloc.into_owned();
        let (stacks, base_tag) = if !memory.extra.validate {
            (None, Tag::Untagged)
        } else {
            let (stacks, base_tag) = Stacks::new_allocation(
                id,
                Size::from_bytes(alloc.bytes.len() as u64),
                Rc::clone(&memory.extra.stacked_borrows),
                kind,
            );
            (Some(stacks), base_tag)
        };
        if kind != MiriMemoryKind::Static.into() {
            assert!(alloc.relocations.is_empty(), "Only statics can come initialized with inner pointers");
            // Now we can rely on the inner pointers being static, too.
        }
        let mut memory_extra = memory.extra.stacked_borrows.borrow_mut();
        let alloc: Allocation<Tag, Self::AllocExtra> = Allocation {
            bytes: alloc.bytes,
            relocations: Relocations::from_presorted(
                alloc.relocations.iter()
                    // The allocations in the relocations (pointers stored *inside* this allocation)
                    // all get the base pointer tag.
                    .map(|&(offset, ((), alloc))| {
                        let tag = if !memory.extra.validate {
                            Tag::Untagged
                        } else {
                            memory_extra.static_base_ptr(alloc)
                        };
                        (offset, (tag, alloc))
                    })
                    .collect()
            ),
            undef_mask: alloc.undef_mask,
            align: alloc.align,
            mutability: alloc.mutability,
            extra: AllocExtra {
                stacked_borrows: stacks,
            },
        };
        (Cow::Owned(alloc), base_tag)
    }

    #[inline(always)]
    fn tag_static_base_pointer(
        id: AllocId,
        memory: &Memory<'mir, 'tcx, Self>,
    ) -> Self::PointerTag {
        if !memory.extra.validate {
            Tag::Untagged
        } else {
            memory.extra.stacked_borrows.borrow_mut().static_base_ptr(id)
        }
    }

    #[inline(always)]
    fn retag(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        kind: mir::RetagKind,
        place: PlaceTy<'tcx, Tag>,
    ) -> InterpResult<'tcx> {
        if !Self::enforce_validity(ecx) {
            // No tracking.
             Ok(())
        } else {
            ecx.retag(kind, place)
        }
    }

    #[inline(always)]
    fn stack_push(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
    ) -> InterpResult<'tcx, stacked_borrows::CallId> {
        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().new_call())
    }

    #[inline(always)]
    fn stack_pop(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        extra: stacked_borrows::CallId,
    ) -> InterpResult<'tcx> {
        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().end_call(extra))
    }

    fn int_to_ptr(
        int: u64,
        memory: &Memory<'mir, 'tcx, Self>,
    ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
        if int == 0 {
            err!(InvalidNullPointerUsage)
        } else if memory.extra.rng.is_none() {
            err!(ReadBytesAsPointer)
        } else {
           intptrcast::GlobalState::int_to_ptr(int, memory)
        }
    }

    fn ptr_to_int(
        ptr: Pointer<Self::PointerTag>,
        memory: &Memory<'mir, 'tcx, Self>,
    ) -> InterpResult<'tcx, u64> {
        if memory.extra.rng.is_none() {
            err!(ReadPointerAsBytes)
        } else {
            intptrcast::GlobalState::ptr_to_int(ptr, memory)
        }
    }
}

impl AllocationExtra<Tag> for AllocExtra {
    #[inline(always)]
    fn memory_read<'tcx>(
        alloc: &Allocation<Tag, AllocExtra>,
        ptr: Pointer<Tag>,
        size: Size,
    ) -> InterpResult<'tcx> {
        if let Some(ref stacked_borrows) = alloc.extra.stacked_borrows {
            stacked_borrows.memory_read(ptr, size)
        } else {
            Ok(())
        }
    }

    #[inline(always)]
    fn memory_written<'tcx>(
        alloc: &mut Allocation<Tag, AllocExtra>,
        ptr: Pointer<Tag>,
        size: Size,
    ) -> InterpResult<'tcx> {
        if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
            stacked_borrows.memory_written(ptr, size)
        } else {
            Ok(())
        }
    }

    #[inline(always)]
    fn memory_deallocated<'tcx>(
        alloc: &mut Allocation<Tag, AllocExtra>,
        ptr: Pointer<Tag>,
        size: Size,
    ) -> InterpResult<'tcx> {
        if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
            stacked_borrows.memory_deallocated(ptr, size)
        } else {
            Ok(())
        }
    }
}

impl MayLeak for MiriMemoryKind {
    #[inline(always)]
    fn may_leak(self) -> bool {
        use self::MiriMemoryKind::*;
        match self {
            Rust | C | WinHeap => false,
            Env | Static => true,
        }
    }
}
<commit_msg>fix for changd machine trait signatures<commit_after>//! Global machine state as well as implementation of the interpreter engine
//! `Machine` trait.

use std::rc::Rc;
use std::borrow::Cow;
use std::collections::HashMap;
use std::cell::RefCell;

use rand::rngs::StdRng;

use syntax::attr;
use syntax::symbol::sym;
use rustc::hir::def_id::DefId;
use rustc::ty::{self, layout::{Size, LayoutOf}, TyCtxt};
use rustc::mir;

use crate::*;

// Some global facts about the emulated machine.
pub const PAGE_SIZE: u64 = 4*1024; // FIXME: adjust to target architecture
pub const STACK_ADDR: u64 = 32*PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
pub const STACK_SIZE: u64 = 16*PAGE_SIZE; // whatever
pub const NUM_CPUS: u64 = 1;

/// Extra memory kinds
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MiriMemoryKind {
    /// `__rust_alloc` memory.
    Rust,
    /// `malloc` memory.
    C,
    /// Windows `HeapAlloc` memory.
    WinHeap,
    /// Part of env var emulation.
    Env,
    /// Statics.
    Static,
}

impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
    #[inline(always)]
    fn into(self) -> MemoryKind<MiriMemoryKind> {
        MemoryKind::Machine(self)
    }
}

/// Extra per-allocation data
#[derive(Debug, Clone)]
pub struct AllocExtra {
    /// Stacked Borrows state is only added if validation is enabled.
    pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
}

/// Extra global memory data
#[derive(Clone, Debug)]
pub struct MemoryExtra {
    pub stacked_borrows: stacked_borrows::MemoryExtra,
    pub intptrcast: intptrcast::MemoryExtra,

    /// The random number generator to use if Miri is running in non-deterministic mode and to
    /// enable intptrcast
    pub(crate) rng: Option<RefCell<StdRng>>,

    /// Whether to enforce the validity invariant.
    pub(crate) validate: bool,
}

impl MemoryExtra {
    pub fn new(rng: Option<StdRng>, validate: bool) -> Self {
        MemoryExtra {
            stacked_borrows: Default::default(),
            intptrcast: Default::default(),
            rng: rng.map(RefCell::new),
            validate,
        }
    }
}

/// The machine itself.
pub struct Evaluator<'tcx> {
    /// Environment variables set by `setenv`.
    /// Miri does not expose env vars from the host to the emulated program.
    pub(crate) env_vars: HashMap<Vec<u8>, Pointer<Tag>>,

    /// Program arguments (`Option` because we can only initialize them after creating the ecx).
    /// These are *pointers* to argc/argv because macOS.
    /// We also need the full command line as one string because of Windows.
    pub(crate) argc: Option<Pointer<Tag>>,
    pub(crate) argv: Option<Pointer<Tag>>,
    pub(crate) cmd_line: Option<Pointer<Tag>>,

    /// Last OS error.
    pub(crate) last_error: u32,

    /// TLS state.
    pub(crate) tls: TlsData<'tcx>,
}

impl<'tcx> Evaluator<'tcx> {
    pub(crate) fn new() -> Self {
        Evaluator {
            env_vars: HashMap::default(),
            argc: None,
            argv: None,
            cmd_line: None,
            last_error: 0,
            tls: TlsData::default(),
        }
    }
}

/// A rustc InterpCx for Miri.
pub type MiriEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, Evaluator<'tcx>>;

/// A little trait that's useful to be inherited by extension traits.
pub trait MiriEvalContextExt<'mir, 'tcx> {
    fn eval_context_ref(&self) -> &MiriEvalContext<'mir, 'tcx>;
    fn eval_context_mut(&mut self) -> &mut MiriEvalContext<'mir, 'tcx>;
}
impl<'mir, 'tcx> MiriEvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx> {
    #[inline(always)]
    fn eval_context_ref(&self) -> &MiriEvalContext<'mir, 'tcx> {
        self
    }
    #[inline(always)]
    fn eval_context_mut(&mut self) -> &mut MiriEvalContext<'mir, 'tcx> {
        self
    }
}

/// Machine hook implementations.
impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
    type MemoryKinds = MiriMemoryKind;

    type FrameExtra = stacked_borrows::CallId;
    type MemoryExtra = MemoryExtra;
    type AllocExtra = AllocExtra;
    type PointerTag = Tag;
    type ExtraFnVal = Dlsym;

    type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Tag, Self::AllocExtra>)>;

    const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Static);

    #[inline(always)]
    fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
        ecx.memory().extra.validate
    }

    #[inline(always)]
    fn find_fn(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
        ecx.find_fn(instance, args, dest, ret)
    }

    #[inline(always)]
    fn call_extra_fn(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        fn_val: Dlsym,
        args: &[OpTy<'tcx, Tag>],
        dest: Option<PlaceTy<'tcx, Tag>>,
        ret: Option<mir::BasicBlock>,
    ) -> InterpResult<'tcx> {
        ecx.call_dlsym(fn_val, args, dest, ret)
    }

    #[inline(always)]
    fn call_intrinsic(
        ecx: &mut rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
        instance: ty::Instance<'tcx>,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
    ) -> InterpResult<'tcx> {
        ecx.call_intrinsic(instance, args, dest)
    }

    #[inline(always)]
    fn ptr_op(
        ecx: &rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
        bin_op: mir::BinOp,
        left: ImmTy<'tcx, Tag>,
        right: ImmTy<'tcx, Tag>,
    ) -> InterpResult<'tcx, (Scalar<Tag>, bool)> {
        ecx.ptr_op(bin_op, left, right)
    }

    fn box_alloc(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        dest: PlaceTy<'tcx, Tag>,
    ) -> InterpResult<'tcx> {
        trace!("box_alloc for {:?}", dest.layout.ty);
        // Call the `exchange_malloc` lang item.
        let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
        let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
        let malloc_mir = ecx.load_mir(malloc.def)?;
        ecx.push_stack_frame(
            malloc,
            malloc_mir.span,
            malloc_mir,
            Some(dest),
            // Don't do anything when we are done. The `statement()` function will increment
            // the old stack frame's stmt counter to the next statement, which means that when
            // `exchange_malloc` returns, we go on evaluating exactly where we want to be.
            StackPopCleanup::None { cleanup: true },
        )?;

        let mut args = ecx.frame().body.args_iter();
        let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;

        // First argument: `size`.
        // (`0` is allowed here -- this is expected to be handled by the lang item).
        let arg = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
        let size = layout.size.bytes();
        ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;

        // Second argument: `align`.
        let arg = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
        let align = layout.align.abi.bytes();
        ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;

        // No more arguments.
        assert!(
            args.next().is_none(),
            "`exchange_malloc` lang item has more arguments than expected"
        );
        Ok(())
    }

    fn find_foreign_static(
        tcx: TyCtxt<'tcx>,
        def_id: DefId,
    ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> {
        let attrs = tcx.get_attrs(def_id);
        let link_name = match attr::first_attr_value_str_by_name(&attrs, sym::link_name) {
            Some(name) => name.as_str(),
            None => tcx.item_name(def_id).as_str(),
        };

        let alloc = match link_name.get() {
            "__cxa_thread_atexit_impl" => {
                // This should be all-zero, pointer-sized.
                let size = tcx.data_layout.pointer_size;
                let data = vec![0; size.bytes() as usize];
                Allocation::from_bytes(&data, tcx.data_layout.pointer_align.abi)
            }
            _ => return err!(Unimplemented(
                    format!("can't access foreign static: {}", link_name),
                )),
        };
        Ok(Cow::Owned(alloc))
    }

    #[inline(always)]
    fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>
    {
        // We are not interested in detecting loops.
        Ok(())
    }

    fn tag_allocation<'b>(
        memory_extra: &MemoryExtra,
        id: AllocId,
        alloc: Cow<'b, Allocation>,
        kind: Option<MemoryKind<Self::MemoryKinds>>,
    ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
        let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
        let alloc = alloc.into_owned();
        let (stacks, base_tag) = if !memory_extra.validate {
            (None, Tag::Untagged)
        } else {
            let (stacks, base_tag) = Stacks::new_allocation(
                id,
                Size::from_bytes(alloc.bytes.len() as u64),
                Rc::clone(&memory_extra.stacked_borrows),
                kind,
            );
            (Some(stacks), base_tag)
        };
        if kind != MiriMemoryKind::Static.into() {
            assert!(alloc.relocations.is_empty(), "Only statics can come initialized with inner pointers");
            // Now we can rely on the inner pointers being static, too.
        }
        let mut stacked_borrows = memory_extra.stacked_borrows.borrow_mut();
        let alloc: Allocation<Tag, Self::AllocExtra> = Allocation {
            bytes: alloc.bytes,
            relocations: Relocations::from_presorted(
                alloc.relocations.iter()
                    // The allocations in the relocations (pointers stored *inside* this allocation)
                    // all get the base pointer tag.
                    .map(|&(offset, ((), alloc))| {
                        let tag = if !memory_extra.validate {
                            Tag::Untagged
                        } else {
                            stacked_borrows.static_base_ptr(alloc)
                        };
                        (offset, (tag, alloc))
                    })
                    .collect()
            ),
            undef_mask: alloc.undef_mask,
            align: alloc.align,
            mutability: alloc.mutability,
            extra: AllocExtra {
                stacked_borrows: stacks,
            },
        };
        (Cow::Owned(alloc), base_tag)
    }

    #[inline(always)]
    fn tag_static_base_pointer(
        memory_extra: &MemoryExtra,
        id: AllocId,
    ) -> Self::PointerTag {
        if !memory_extra.validate {
            Tag::Untagged
        } else {
            memory_extra.stacked_borrows.borrow_mut().static_base_ptr(id)
        }
    }

    #[inline(always)]
    fn retag(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        kind: mir::RetagKind,
        place: PlaceTy<'tcx, Tag>,
    ) -> InterpResult<'tcx> {
        if !Self::enforce_validity(ecx) {
            // No tracking.
             Ok(())
        } else {
            ecx.retag(kind, place)
        }
    }

    #[inline(always)]
    fn stack_push(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
    ) -> InterpResult<'tcx, stacked_borrows::CallId> {
        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().new_call())
    }

    #[inline(always)]
    fn stack_pop(
        ecx: &mut InterpCx<'mir, 'tcx, Self>,
        extra: stacked_borrows::CallId,
    ) -> InterpResult<'tcx> {
        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().end_call(extra))
    }

    fn int_to_ptr(
        memory: &Memory<'mir, 'tcx, Self>,
        int: u64,
    ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
        if int == 0 {
            err!(InvalidNullPointerUsage)
        } else if memory.extra.rng.is_none() {
            err!(ReadBytesAsPointer)
        } else {
           intptrcast::GlobalState::int_to_ptr(int, memory)
        }
    }

    fn ptr_to_int(
        memory: &Memory<'mir, 'tcx, Self>,
        ptr: Pointer<Self::PointerTag>,
    ) -> InterpResult<'tcx, u64> {
        if memory.extra.rng.is_none() {
            err!(ReadPointerAsBytes)
        } else {
            intptrcast::GlobalState::ptr_to_int(ptr, memory)
        }
    }
}

impl AllocationExtra<Tag> for AllocExtra {
    #[inline(always)]
    fn memory_read<'tcx>(
        alloc: &Allocation<Tag, AllocExtra>,
        ptr: Pointer<Tag>,
        size: Size,
    ) -> InterpResult<'tcx> {
        if let Some(ref stacked_borrows) = alloc.extra.stacked_borrows {
            stacked_borrows.memory_read(ptr, size)
        } else {
            Ok(())
        }
    }

    #[inline(always)]
    fn memory_written<'tcx>(
        alloc: &mut Allocation<Tag, AllocExtra>,
        ptr: Pointer<Tag>,
        size: Size,
    ) -> InterpResult<'tcx> {
        if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
            stacked_borrows.memory_written(ptr, size)
        } else {
            Ok(())
        }
    }

    #[inline(always)]
    fn memory_deallocated<'tcx>(
        alloc: &mut Allocation<Tag, AllocExtra>,
        ptr: Pointer<Tag>,
        size: Size,
    ) -> InterpResult<'tcx> {
        if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
            stacked_borrows.memory_deallocated(ptr, size)
        } else {
            Ok(())
        }
    }
}

impl MayLeak for MiriMemoryKind {
    #[inline(always)]
    fn may_leak(self) -> bool {
        use self::MiriMemoryKind::*;
        match self {
            Rust | C | WinHeap => false,
            Env | Static => true,
        }
    }
}
<|endoftext|>


<commit_before>use devices::DevicesDisplayInfo;
use devices::{Devices, JoystickState};
use event::RawEvent;
use rawinput::{get_event, get_joystick_state};
use registrar;
use std::time::{SystemTime, UNIX_EPOCH};
use winapi::shared::minwindef::UINT;
use winapi::shared::windef::HWND;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::{
    CreateWindowExW, DefWindowProcW, RegisterClassExW, CW_USEDEFAULT, HWND_MESSAGE, WNDCLASSEXW,
};

use std::collections::VecDeque;
use std::ffi::OsStr;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use std::thread;
use std::thread::JoinHandle;

use std::sync::mpsc::{channel, Receiver, Sender};

enum Command {
    Register(DeviceType),
    GetEvent,
    GetJoystickState(usize),
    Finish,
    PrintDeviceList,
    GetDeviceList,
    GetDeviceStats,
}

/// Types of Raw Input Device
#[derive(PartialEq, Eq, Clone)]
pub enum DeviceType {
    Mice,
    Keyboards,
    Joysticks(XInputInclude),
}

/// Denotes if Xbox360 controllers should be used
/// Please Note: Rawinput support for official Xbox360 controllers
/// is very limited (triggers share same axis, no support for
/// rumble or the central X button)
/// Please see https://en.wikipedia.org/wiki/DirectInput#Xbox_360_Controller_support
/// for more details
#[derive(PartialEq, Eq, Clone)]
pub enum XInputInclude {
    True,
    False,
}

#[derive(Default)]
pub struct DeviceStats {
    pub number_of_mice: usize,
    pub number_of_keyboards: usize,
    pub number_of_joysticks: usize,
}

/// Manages Raw Input Processing
pub struct RawInputManager {
    joiner: Option<JoinHandle<()>>,
    sender: Sender<Command>,
    receiver: Receiver<Option<RawEvent>>,
    joystick_receiver: Receiver<Option<JoystickState>>,
    device_info_receiver: Receiver<DevicesDisplayInfo>,
    device_stats_receiver: Receiver<DeviceStats>,
}

impl RawInputManager {
    pub fn new() -> Result<RawInputManager, &'static str> {
        let (tx, rx) = channel();
        let (tx2, rx2) = channel();
        let (tx_joy, rx_joy) = channel();
        let (tx_devices, rx_devices) = channel();
        let (tx_stats, rx_stats) = channel();

        let joiner = thread::spawn(move || {
            let hwnd = setup_message_window();
            let mut event_queue = VecDeque::new();
            let mut devices = Devices::new();
            let mut exit = false;
            let mut registrar = registrar::RawInputRegistrar::new();
            while !exit {
                match rx.recv().unwrap() {
                    Command::Register(thing) => {
                        devices = registrar.register_devices(hwnd, thing).unwrap();
                        tx2.send(None).unwrap();
                    }
                    Command::GetEvent => {
                        tx2.send(get_event(&mut event_queue, &mut devices)).unwrap()
                    }
                    Command::Finish => {
                        exit = true;
                    }
                    Command::GetJoystickState(id) => {
                        tx_joy.send(get_joystick_state(&devices, id)).unwrap()
                    }
                    Command::PrintDeviceList => print_raw_device_list(&devices),
                    Command::GetDeviceList => tx_devices.send(devices.clone().into()).unwrap(),
                    Command::GetDeviceStats => tx_stats.send(get_device_stats(&devices)).unwrap(),
                };
            }
        });
        Ok(RawInputManager {
            joiner: Some(joiner),
            sender: tx,
            receiver: rx2,
            joystick_receiver: rx_joy,
            device_stats_receiver: rx_stats,
            device_info_receiver: rx_devices
        })
    }

    /// Allows Raw Input devices of type device_type to be received from the Input Manager
    pub fn register_devices(&mut self, device_type: DeviceType) {
        self.sender.send(Command::Register(device_type)).unwrap();
        self.receiver.recv().unwrap();
    }

    /// Get Event from the Input Manager
    pub fn get_event(&mut self) -> Option<RawEvent> {
        self.sender.send(Command::GetEvent).unwrap();
        self.receiver.recv().unwrap()
    }

    /// Get Joystick State from the Input Manager
    pub fn get_joystick_state(&mut self, id: usize) -> Option<JoystickState> {
        self.sender.send(Command::GetJoystickState(id)).unwrap();
        self.joystick_receiver.recv().unwrap()
    }

    /// Print List of Potential Input Devices
    pub fn print_device_list(&self) {
        self.sender.send(Command::PrintDeviceList).unwrap();
    }

    /// Get Device Stats (number of connected devices)
    pub fn get_device_stats(&self) -> DeviceStats {
        self.sender.send(Command::GetDeviceStats).unwrap();
        self.device_stats_receiver.recv().unwrap()
    }

    /// Get Device list
    pub fn get_device_list(&self) -> DevicesDisplayInfo {
            self.sender.send(Command::GetDeviceList).unwrap();
            self.device_info_receiver.recv().unwrap()
    }
}

impl Drop for RawInputManager {
    fn drop(&mut self) {
        self.sender.send(Command::Finish).unwrap();
        self.joiner.take().unwrap().join().unwrap();
    }
}

fn setup_message_window() -> HWND {
    let hwnd: HWND;
    unsafe {
        let hinstance = GetModuleHandleW(ptr::null());
        if hinstance == ptr::null_mut() {
            panic!("Instance Generation Failed");
        }

        let current_time = SystemTime::now();
        let classname_str = format!(
            "RawInput Hidden Window - {:?}",
            current_time.duration_since(UNIX_EPOCH).unwrap()
        );

        let classname = OsStr::new(&classname_str)
            .encode_wide()
            .chain(Some(0).into_iter())
            .collect::<Vec<_>>();

        let wcex = WNDCLASSEXW {
            cbSize: (mem::size_of::<WNDCLASSEXW>()) as UINT,
            cbClsExtra: 0,
            cbWndExtra: 0,
            hbrBackground: ptr::null_mut(),
            hCursor: ptr::null_mut(),
            hIcon: ptr::null_mut(),
            hIconSm: ptr::null_mut(),
            hInstance: hinstance,
            lpfnWndProc: Some(DefWindowProcW),
            lpszClassName: classname.as_ptr(),
            lpszMenuName: ptr::null_mut(),
            style: 0,
        };
        let a = RegisterClassExW(&wcex);
        if a == 0 {
            panic!("Registering WindowClass Failed!");
        }

        hwnd = CreateWindowExW(
            0,
            classname.as_ptr(),
            classname.as_ptr(),
            0,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            HWND_MESSAGE,
            ptr::null_mut(),
            hinstance,
            ptr::null_mut(),
        );
        if hwnd.is_null() {
            panic!("Window Creation Failed!");
        }
    }
    hwnd
}

/// Prints a list of all available raw input devices
fn print_raw_device_list(devices: &Devices) {
    println!("Mice:");
    for mouse in &devices.mice {
        println!("{:?}", mouse);
    }
    println!("Keyboards:");
    for keyboard in &devices.keyboards {
        println!("{:?}", keyboard);
    }
    println!("Hids:");
    for joystick in &devices.joysticks {
        println!("{:?}", joystick);
    }
}

fn get_device_stats(devices: &Devices) -> DeviceStats {
    DeviceStats {
        number_of_mice: devices.mice.len(),
        number_of_keyboards: devices.keyboards.len(),
        number_of_joysticks: devices.joysticks.len(),
    }
}
<commit_msg>Getting input events no longer blocks<commit_after>use devices::DevicesDisplayInfo;
use devices::{Devices, JoystickState};
use event::RawEvent;
use std::sync::mpsc::TryRecvError;
use rawinput::{get_event, get_joystick_state};
use registrar;
use std::time::{SystemTime, UNIX_EPOCH};
use winapi::shared::minwindef::UINT;
use winapi::shared::windef::HWND;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::{
    CreateWindowExW, DefWindowProcW, RegisterClassExW, CW_USEDEFAULT, HWND_MESSAGE, WNDCLASSEXW,
};

use std::collections::VecDeque;
use std::ffi::OsStr;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use std::thread;
use std::thread::JoinHandle;

use std::sync::mpsc::{channel, Receiver, Sender};

enum Command {
    Register(DeviceType),
    GetEvent,
    GetJoystickState(usize),
    Finish,
    PrintDeviceList,
    GetDeviceList,
    GetDeviceStats,
}

/// Types of Raw Input Device
#[derive(PartialEq, Eq, Clone)]
pub enum DeviceType {
    Mice,
    Keyboards,
    Joysticks(XInputInclude),
}

/// Denotes if Xbox360 controllers should be used
/// Please Note: Rawinput support for official Xbox360 controllers
/// is very limited (triggers share same axis, no support for
/// rumble or the central X button)
/// Please see https://en.wikipedia.org/wiki/DirectInput#Xbox_360_Controller_support
/// for more details
#[derive(PartialEq, Eq, Clone)]
pub enum XInputInclude {
    True,
    False,
}

#[derive(Default)]
pub struct DeviceStats {
    pub number_of_mice: usize,
    pub number_of_keyboards: usize,
    pub number_of_joysticks: usize,
}

/// Manages Raw Input Processing
pub struct RawInputManager {
    joiner: Option<JoinHandle<()>>,
    sender: Sender<Command>,
    receiver: Receiver<Option<RawEvent>>,
    joystick_receiver: Receiver<Option<JoystickState>>,
    device_info_receiver: Receiver<DevicesDisplayInfo>,
    device_stats_receiver: Receiver<DeviceStats>,
}

impl RawInputManager {
    pub fn new() -> Result<RawInputManager, &'static str> {
        let (tx, rx) = channel();
        let (tx2, rx2) = channel();
        let (tx_joy, rx_joy) = channel();
        let (tx_devices, rx_devices) = channel();
        let (tx_stats, rx_stats) = channel();

        let joiner = thread::spawn(move || {
            let hwnd = setup_message_window();
            let mut event_queue = VecDeque::new();
            let mut devices = Devices::new();
            let mut exit = false;
            let mut registrar = registrar::RawInputRegistrar::new();
            while !exit {
                match rx.try_recv() {
                    Err(TryRecvError::Disconnected) => {
                        panic!("Multinput Thread Unexpectedly Disconnected!")
                    },
                    Err(TryRecvError::Empty) => {
                        std::thread::sleep(std::time::Duration::from_nanos(1));
                    },
                    Ok(Command::Register(thing)) => {
                        devices = registrar.register_devices(hwnd, thing).unwrap();
                        tx2.send(None).unwrap();
                    }
                    Ok(Command::GetEvent) => {
                        tx2.send(get_event(&mut event_queue, &mut devices)).unwrap()
                    }
                    Ok(Command::Finish) => {
                        exit = true;
                    }
                    Ok(Command::GetJoystickState(id)) => {
                        tx_joy.send(get_joystick_state(&devices, id)).unwrap()
                    }
                    Ok(Command::PrintDeviceList) => print_raw_device_list(&devices),
                    Ok(Command::GetDeviceList) => tx_devices.send(devices.clone().into()).unwrap(),
                    Ok(Command::GetDeviceStats) => tx_stats.send(get_device_stats(&devices)).unwrap(),
                };
            }
        });
        Ok(RawInputManager {
            joiner: Some(joiner),
            sender: tx,
            receiver: rx2,
            joystick_receiver: rx_joy,
            device_stats_receiver: rx_stats,
            device_info_receiver: rx_devices
        })
    }

    /// Allows Raw Input devices of type device_type to be received from the Input Manager
    pub fn register_devices(&mut self, device_type: DeviceType) {
        self.sender.send(Command::Register(device_type)).unwrap();
        self.receiver.recv().unwrap();
    }

    /// Get Event from the Input Manager
    pub fn get_event(&mut self) -> Option<RawEvent> {
        self.sender.send(Command::GetEvent).unwrap();
        match self.receiver.try_recv() {
            Ok(event) => event,
            _ => None
        }
    }

    /// Get Joystick State from the Input Manager
    pub fn get_joystick_state(&mut self, id: usize) -> Option<JoystickState> {
        self.sender.send(Command::GetJoystickState(id)).unwrap();
        self.joystick_receiver.recv().unwrap()
    }

    /// Print List of Potential Input Devices
    pub fn print_device_list(&self) {
        self.sender.send(Command::PrintDeviceList).unwrap();
    }

    /// Get Device Stats (number of connected devices)
    pub fn get_device_stats(&self) -> DeviceStats {
        self.sender.send(Command::GetDeviceStats).unwrap();
        self.device_stats_receiver.recv().unwrap()
    }

    /// Get Device list
    pub fn get_device_list(&self) -> DevicesDisplayInfo {
            self.sender.send(Command::GetDeviceList).unwrap();
            self.device_info_receiver.recv().unwrap()
    }
}

impl Drop for RawInputManager {
    fn drop(&mut self) {
        self.sender.send(Command::Finish).unwrap();
        self.joiner.take().unwrap().join().unwrap();
    }
}

fn setup_message_window() -> HWND {
    let hwnd: HWND;
    unsafe {
        let hinstance = GetModuleHandleW(ptr::null());
        if hinstance == ptr::null_mut() {
            panic!("Instance Generation Failed");
        }

        let current_time = SystemTime::now();
        let classname_str = format!(
            "RawInput Hidden Window - {:?}",
            current_time.duration_since(UNIX_EPOCH).unwrap()
        );

        let classname = OsStr::new(&classname_str)
            .encode_wide()
            .chain(Some(0).into_iter())
            .collect::<Vec<_>>();

        let wcex = WNDCLASSEXW {
            cbSize: (mem::size_of::<WNDCLASSEXW>()) as UINT,
            cbClsExtra: 0,
            cbWndExtra: 0,
            hbrBackground: ptr::null_mut(),
            hCursor: ptr::null_mut(),
            hIcon: ptr::null_mut(),
            hIconSm: ptr::null_mut(),
            hInstance: hinstance,
            lpfnWndProc: Some(DefWindowProcW),
            lpszClassName: classname.as_ptr(),
            lpszMenuName: ptr::null_mut(),
            style: 0,
        };
        let a = RegisterClassExW(&wcex);
        if a == 0 {
            panic!("Registering WindowClass Failed!");
        }

        hwnd = CreateWindowExW(
            0,
            classname.as_ptr(),
            classname.as_ptr(),
            0,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            HWND_MESSAGE,
            ptr::null_mut(),
            hinstance,
            ptr::null_mut(),
        );
        if hwnd.is_null() {
            panic!("Window Creation Failed!");
        }
    }
    hwnd
}

/// Prints a list of all available raw input devices
fn print_raw_device_list(devices: &Devices) {
    println!("Mice:");
    for mouse in &devices.mice {
        println!("{:?}", mouse);
    }
    println!("Keyboards:");
    for keyboard in &devices.keyboards {
        println!("{:?}", keyboard);
    }
    println!("Hids:");
    for joystick in &devices.joysticks {
        println!("{:?}", joystick);
    }
}

fn get_device_stats(devices: &Devices) -> DeviceStats {
    DeviceStats {
        number_of_mice: devices.mice.len(),
        number_of_keyboards: devices.keyboards.len(),
        number_of_joysticks: devices.joysticks.len(),
    }
}
<|endoftext|>


<commit_before>use code::Code;

/// Error generated by the parser.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum ParseError {
    /// String was empty.
    EmptyCommand,
    /// Message did not have a code.
    EmptyMessage,
    /// Unexpected end of the string.
    UnexpectedEnd,
}

/// Represents a message received from the server.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Message {
    /// Prefix
    pub prefix: Option<Prefix>,
    /// Code
    pub code: Code,
    /// Arguments
    pub args: Vec<String>,
    /// Suffix
    pub suffix: Option<String>,
}

impl Message {

    /// Parse the given string into a `Message` struct.
    ///
    /// An error is returned if the message is not valid.
    pub fn parse(line: &str) -> Result<Message, ParseError> {
        if line.len() == 0 || line.trim().len() == 0 {
            return Err(ParseError::EmptyMessage);
        }

        let mut state = line.trim_right_matches("\r\n");
        let mut prefix: Option<Prefix> = None;
        let code: Option<String>;
        let mut args: Vec<String> = Vec::new();
        let mut suffix: Option<String> = None;

        // Look for a prefix
        if state.starts_with(":") {
            match state.find(" ") {
                None => return Err(ParseError::UnexpectedEnd),
                Some(idx) => {
                    prefix = parse_prefix(&state[1..idx]);
                    state = &state[idx + 1..];
                }
            }
        }

        // Look for the command/reply
        match state.find(" ") {
            None => {
                if state.len() == 0 {
                    return Err(ParseError::EmptyMessage);
                } else {
                    code = Some(state[..].to_string());
                    state = &state[state.len()..];
                }
            }
            Some(idx) => {
                code = Some(state[..idx].to_string());
                state = &state[idx + 1..];
            }
        }

        // Look for arguments and the suffix
        if state.len() > 0 {
            loop {
                if state.starts_with(":") {
                    suffix = Some(state[1..].to_string());
                    break;
                } else {
                    match state.find(" ") {
                        None => {
                            args.push(state[..].to_string());
                            break;
                        }
                        Some(idx) => {
                            args.push(state[..idx].to_string());
                            state = &state[idx + 1..];
                        }
                    }
                }
            }
        }

        let code = match code {
            None => return Err(ParseError::EmptyCommand),
            Some(text) => {
                match text.parse() {
                    Ok(code) => code,
                    Err(_) => Code::Unknown(text.to_string()),
                }
            }
        };

        Ok(Message {
            prefix: prefix,
            code: code,
            args: args,
            suffix: suffix,
        })
    }
}

fn parse_prefix(prefix: &str) -> Option<Prefix> {
    match prefix.find("!") {
        None => Some(Prefix::Server(prefix.to_string())),
        Some(excpos) => {
            let nick = &prefix[..excpos];
            let rest = &prefix[excpos + 1..];
            match rest.find("@") {
                None => return None,
                Some(atpos) => {
                    let user = &rest[..atpos];
                    let host = &rest[atpos + 1..];
                    return Some(Prefix::User(User::new(nick, user, host)));
                }
            }
        }
    }
}

/// Prefix of the message.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Prefix {
    /// Prefix is a user.
    User(User),
    /// Prefix is a server.
    Server(String),
}

/// User prefix representation.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct User {
    nick: String,
    user: String,
    host: String,
}

impl User {

    pub fn new(nick: &str, user: &str, host: &str) -> User {
        User {
            nick: nick.to_string(),
            user: user.to_string(),
            host: host.to_string(),
        }
    }

}

#[test]
fn test_full() {
    let res = Message::parse(":org.prefix.cool COMMAND arg1 arg2 arg3 :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.code, Code::Unknown("COMMAND".to_string()));
    assert_eq!(msg.args, vec!["arg1", "arg2", "arg3"]);
    assert_eq!(msg.suffix, Some("suffix is pretty cool yo".to_string()));
}

#[test]
fn test_no_prefix() {
    let res = Message::parse("NICK arg1 arg2 arg3 :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, None);
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args, vec!["arg1", "arg2", "arg3"]);
    assert_eq!(msg.suffix, Some("suffix is pretty cool yo".to_string()));
}

#[test]
fn test_no_suffix() {
    let res = Message::parse(":org.prefix.cool NICK arg1 arg2 arg3");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args, vec!["arg1", "arg2", "arg3"]);
    assert_eq!(msg.suffix, None);
}

#[test]
fn test_no_args() {
    let res = Message::parse(":org.prefix.cool NICK :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args.len(), 0);
    assert_eq!(msg.suffix, Some("suffix is pretty cool yo".to_string()));
}

#[test]
fn test_only_command() {
    let res = Message::parse("NICK");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, None);
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args.len(), 0);
    assert_eq!(msg.suffix, None);
}

#[test]
fn test_empty_message() {
    let res = Message::parse("");
    assert!(res.is_err());
    let err = res.err().unwrap();
    assert!(err == ParseError::EmptyMessage);
}

#[test]
fn test_empty_message_trim() {
    let res = Message::parse("    ");
    assert!(res.is_err());
    let err = res.err().unwrap();
    assert!(err == ParseError::EmptyMessage);
}

#[test]
fn test_only_prefix() {
    let res = Message::parse(":org.prefix.cool");
    assert!(res.is_err());
    let err = res.err().unwrap();
    assert!(err == ParseError::UnexpectedEnd);
}

#[test]
fn test_prefix_none() {
    let res = Message::parse("COMMAND :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert!(msg.prefix == None);
}

#[test]
fn test_prefix_server() {
    let res = Message::parse(":irc.freenode.net COMMAND :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, Some(Prefix::Server("irc.freenode.net".to_string())));
}

#[test]
fn test_prefix_user() {
    let res = Message::parse(":bob!bob@bob.com COMMAND :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, Some(Prefix::User(User::new("bob", "bob", "bob.com"))));
}
<commit_msg>Make the User fields public and refresh the constructor<commit_after>use code::Code;

/// Error generated by the parser.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum ParseError {
    /// String was empty.
    EmptyCommand,
    /// Message did not have a code.
    EmptyMessage,
    /// Unexpected end of the string.
    UnexpectedEnd,
}

/// Represents a message received from the server.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Message {
    /// Prefix
    pub prefix: Option<Prefix>,
    /// Code
    pub code: Code,
    /// Arguments
    pub args: Vec<String>,
    /// Suffix
    pub suffix: Option<String>,
}

impl Message {

    /// Parse the given string into a `Message` struct.
    ///
    /// An error is returned if the message is not valid.
    pub fn parse(line: &str) -> Result<Message, ParseError> {
        if line.len() == 0 || line.trim().len() == 0 {
            return Err(ParseError::EmptyMessage);
        }

        let mut state = line.trim_right_matches("\r\n");
        let mut prefix: Option<Prefix> = None;
        let code: Option<String>;
        let mut args: Vec<String> = Vec::new();
        let mut suffix: Option<String> = None;

        // Look for a prefix
        if state.starts_with(":") {
            match state.find(" ") {
                None => return Err(ParseError::UnexpectedEnd),
                Some(idx) => {
                    prefix = parse_prefix(&state[1..idx]);
                    state = &state[idx + 1..];
                }
            }
        }

        // Look for the command/reply
        match state.find(" ") {
            None => {
                if state.len() == 0 {
                    return Err(ParseError::EmptyMessage);
                } else {
                    code = Some(state[..].to_string());
                    state = &state[state.len()..];
                }
            }
            Some(idx) => {
                code = Some(state[..idx].to_string());
                state = &state[idx + 1..];
            }
        }

        // Look for arguments and the suffix
        if state.len() > 0 {
            loop {
                if state.starts_with(":") {
                    suffix = Some(state[1..].to_string());
                    break;
                } else {
                    match state.find(" ") {
                        None => {
                            args.push(state[..].to_string());
                            break;
                        }
                        Some(idx) => {
                            args.push(state[..idx].to_string());
                            state = &state[idx + 1..];
                        }
                    }
                }
            }
        }

        let code = match code {
            None => return Err(ParseError::EmptyCommand),
            Some(text) => {
                match text.parse() {
                    Ok(code) => code,
                    Err(_) => Code::Unknown(text.to_string()),
                }
            }
        };

        Ok(Message {
            prefix: prefix,
            code: code,
            args: args,
            suffix: suffix,
        })
    }
}

fn parse_prefix(prefix: &str) -> Option<Prefix> {
    match prefix.find("!") {
        None => Some(Prefix::Server(prefix.to_string())),
        Some(excpos) => {
            let nick = &prefix[..excpos];
            let rest = &prefix[excpos + 1..];
            match rest.find("@") {
                None => return None,
                Some(atpos) => {
                    let user = &rest[..atpos];
                    let host = &rest[atpos + 1..];
                    return Some(Prefix::User(User::new(nick, user, host)));
                }
            }
        }
    }
}

/// Prefix of the message.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Prefix {
    /// Prefix is a user.
    User(User),
    /// Prefix is a server.
    Server(String),
}

/// User prefix representation.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct User {
    /// Nickname
    pub nick: String,
    /// Username
    pub user: String,
    /// Hostname
    pub host: String,
}

impl User {

    pub fn new(nick: &str, user: &str, host: &str) -> User {
        User {
            nick: nick.into(),
            user: user.into(),
            host: host.into(),
        }
    }

}

#[test]
fn test_full() {
    let res = Message::parse(":org.prefix.cool COMMAND arg1 arg2 arg3 :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.code, Code::Unknown("COMMAND".to_string()));
    assert_eq!(msg.args, vec!["arg1", "arg2", "arg3"]);
    assert_eq!(msg.suffix, Some("suffix is pretty cool yo".to_string()));
}

#[test]
fn test_no_prefix() {
    let res = Message::parse("NICK arg1 arg2 arg3 :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, None);
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args, vec!["arg1", "arg2", "arg3"]);
    assert_eq!(msg.suffix, Some("suffix is pretty cool yo".to_string()));
}

#[test]
fn test_no_suffix() {
    let res = Message::parse(":org.prefix.cool NICK arg1 arg2 arg3");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args, vec!["arg1", "arg2", "arg3"]);
    assert_eq!(msg.suffix, None);
}

#[test]
fn test_no_args() {
    let res = Message::parse(":org.prefix.cool NICK :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args.len(), 0);
    assert_eq!(msg.suffix, Some("suffix is pretty cool yo".to_string()));
}

#[test]
fn test_only_command() {
    let res = Message::parse("NICK");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, None);
    assert_eq!(msg.code, Code::Nick);
    assert_eq!(msg.args.len(), 0);
    assert_eq!(msg.suffix, None);
}

#[test]
fn test_empty_message() {
    let res = Message::parse("");
    assert!(res.is_err());
    let err = res.err().unwrap();
    assert!(err == ParseError::EmptyMessage);
}

#[test]
fn test_empty_message_trim() {
    let res = Message::parse("    ");
    assert!(res.is_err());
    let err = res.err().unwrap();
    assert!(err == ParseError::EmptyMessage);
}

#[test]
fn test_only_prefix() {
    let res = Message::parse(":org.prefix.cool");
    assert!(res.is_err());
    let err = res.err().unwrap();
    assert!(err == ParseError::UnexpectedEnd);
}

#[test]
fn test_prefix_none() {
    let res = Message::parse("COMMAND :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert!(msg.prefix == None);
}

#[test]
fn test_prefix_server() {
    let res = Message::parse(":irc.freenode.net COMMAND :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, Some(Prefix::Server("irc.freenode.net".to_string())));
}

#[test]
fn test_prefix_user() {
    let res = Message::parse(":bob!bob@bob.com COMMAND :suffix is pretty cool yo");
    assert!(res.is_ok());
    let msg = res.ok().unwrap();
    assert_eq!(msg.prefix, Some(Prefix::User(User::new("bob", "bob", "bob.com"))));
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::mem;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

use types::Oid;
use util;

use self::BackendMessage::*;
use self::FrontendMessage::*;

pub const PROTOCOL_VERSION: u32 = 0x0003_0000;
pub const CANCEL_CODE: u32 = 80877102;
pub const SSL_CODE: u32 = 80877103;

pub enum BackendMessage {
    AuthenticationCleartextPassword,
    AuthenticationGSS,
    AuthenticationKerberosV5,
    AuthenticationMD5Password {
        salt: [u8; 4]
    },
    AuthenticationOk,
    AuthenticationSCMCredential,
    AuthenticationSSPI,
    BackendKeyData {
        process_id: u32,
        secret_key: u32
    },
    BindComplete,
    CloseComplete,
    CommandComplete {
        tag: String,
    },
    // FIXME naming
    BCopyData {
        data: Vec<u8>,
    },
    BCopyDone,
    CopyInResponse {
        format: u8,
        column_formats: Vec<u16>,
    },
    CopyOutResponse {
        format: u8,
        column_formats: Vec<u16>,
    },
    DataRow {
        row: Vec<Option<Vec<u8>>>
    },
    EmptyQueryResponse,
    ErrorResponse {
        fields: Vec<(u8, String)>
    },
    NoData,
    NoticeResponse {
        fields: Vec<(u8, String)>
    },
    NotificationResponse {
        pid: u32,
        channel: String,
        payload: String,
    },
    ParameterDescription {
        types: Vec<Oid>
    },
    ParameterStatus {
        parameter: String,
        value: String,
    },
    ParseComplete,
    PortalSuspended,
    ReadyForQuery {
        _state: u8
    },
    RowDescription {
        descriptions: Vec<RowDescriptionEntry>
    }
}

pub struct RowDescriptionEntry {
    pub name: String,
    pub table_oid: Oid,
    pub column_id: i16,
    pub type_oid: Oid,
    pub type_size: i16,
    pub type_modifier: i32,
    pub format: i16
}

pub enum FrontendMessage<'a> {
    Bind {
        portal: &'a str,
        statement: &'a str,
        formats: &'a [i16],
        values: &'a [Option<Vec<u8>>],
        result_formats: &'a [i16]
    },
    CancelRequest {
        code: u32,
        process_id: u32,
        secret_key: u32,
    },
    Close {
        variant: u8,
        name: &'a str
    },
    CopyData {
        data: &'a [u8],
    },
    CopyDone,
    CopyFail {
        message: &'a str
    },
    Describe {
        variant: u8,
        name: &'a str
    },
    Execute {
        portal: &'a str,
        max_rows: i32
    },
    Parse {
        name: &'a str,
        query: &'a str,
        param_types: &'a [Oid]
    },
    PasswordMessage {
        password: &'a str
    },
    Query {
        query: &'a str
    },
    SslRequest {
        code: u32
    },
    StartupMessage {
        version: u32,
        parameters: &'a [(String, String)]
    },
    Sync,
    Terminate
}

#[doc(hidden)]
trait WriteCStr {
    fn write_cstr(&mut self, s: &str) -> io::Result<()>;
}

impl<W: Write> WriteCStr for W {
    fn write_cstr(&mut self, s: &str) -> io::Result<()> {
        try!(self.write_all(s.as_bytes()));
        Ok(try!(self.write_u8(0)))
    }
}

#[doc(hidden)]
pub trait WriteMessage {
    fn write_message(&mut self, &FrontendMessage) -> io::Result<()> ;
}

impl<W: Write> WriteMessage for W {
    fn write_message(&mut self, message: &FrontendMessage) -> io::Result<()> {
        let mut buf = vec![];
        let mut ident = None;

        match *message {
            Bind { portal, statement, formats, values, result_formats } => {
                ident = Some(b'B');
                try!(buf.write_cstr(portal));
                try!(buf.write_cstr(statement));

                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(formats.len()))));
                for &format in formats {
                    try!(buf.write_i16::<BigEndian>(format));
                }

                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(values.len()))));
                for value in values {
                    match *value {
                        None => try!(buf.write_i32::<BigEndian>(-1)),
                        Some(ref value) => {
                            try!(buf.write_i32::<BigEndian>(try!(i32::from_usize(value.len()))));
                            try!(buf.write_all(&**value));
                        }
                    }
                }

                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(result_formats.len()))));
                for &format in result_formats {
                    try!(buf.write_i16::<BigEndian>(format));
                }
            }
            CancelRequest { code, process_id, secret_key } => {
                try!(buf.write_u32::<BigEndian>(code));
                try!(buf.write_u32::<BigEndian>(process_id));
                try!(buf.write_u32::<BigEndian>(secret_key));
            }
            Close { variant, name } => {
                ident = Some(b'C');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            CopyData { data } => {
                ident = Some(b'd');
                try!(buf.write_all(data));
            }
            CopyDone => ident = Some(b'c'),
            CopyFail { message } => {
                ident = Some(b'f');
                try!(buf.write_cstr(message));
            }
            Describe { variant, name } => {
                ident = Some(b'D');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            Execute { portal, max_rows } => {
                ident = Some(b'E');
                try!(buf.write_cstr(portal));
                try!(buf.write_i32::<BigEndian>(max_rows));
            }
            Parse { name, query, param_types } => {
                ident = Some(b'P');
                try!(buf.write_cstr(name));
                try!(buf.write_cstr(query));
                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(param_types.len()))));
                for &ty in param_types {
                    try!(buf.write_u32::<BigEndian>(ty));
                }
            }
            PasswordMessage { password } => {
                ident = Some(b'p');
                try!(buf.write_cstr(password));
            }
            Query { query } => {
                ident = Some(b'Q');
                try!(buf.write_cstr(query));
            }
            StartupMessage { version, parameters } => {
                try!(buf.write_u32::<BigEndian>(version));
                for &(ref k, ref v) in parameters {
                    try!(buf.write_cstr(&**k));
                    try!(buf.write_cstr(&**v));
                }
                try!(buf.write_u8(0));
            }
            SslRequest { code } => try!(buf.write_u32::<BigEndian>(code)),
            Sync => ident = Some(b'S'),
            Terminate => ident = Some(b'X'),
        }

        if let Some(ident) = ident {
            try!(self.write_u8(ident));
        }

        // add size of length value
        if buf.len() > u32::max_value() as usize - mem::size_of::<u32>() {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "value too large to transmit"));
        }
        try!(self.write_u32::<BigEndian>((buf.len() + mem::size_of::<u32>()) as u32));
        try!(self.write_all(&*buf));

        Ok(())
    }
}

#[doc(hidden)]
trait ReadCStr {
    fn read_cstr(&mut self) -> io::Result<String>;
}

impl<R: BufRead> ReadCStr for R {
    fn read_cstr(&mut self) -> io::Result<String> {
        let mut buf = vec![];
        try!(self.read_until(0, &mut buf));
        buf.pop();
        String::from_utf8(buf).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
    }
}

#[doc(hidden)]
pub trait ReadMessage {
    fn read_message(&mut self) -> io::Result<BackendMessage>;
}

impl<R: BufRead> ReadMessage for R {
    fn read_message(&mut self) -> io::Result<BackendMessage> {
        let ident = try!(self.read_u8());

        // subtract size of length value
        let len = try!(self.read_u32::<BigEndian>()) - mem::size_of::<u32>() as u32;
        let mut rdr = self.by_ref().take(len as u64);

        let ret = match ident {
            b'1' => ParseComplete,
            b'2' => BindComplete,
            b'3' => CloseComplete,
            b'A' => NotificationResponse {
                pid: try!(rdr.read_u32::<BigEndian>()),
                channel: try!(rdr.read_cstr()),
                payload: try!(rdr.read_cstr())
            },
            b'c' => BCopyDone,
            b'C' => CommandComplete { tag: try!(rdr.read_cstr()) },
            b'd' => {
                let mut data = vec![];
                try!(rdr.read_to_end(&mut data));
                BCopyData {
                    data: data,
                }
            }
            b'D' => try!(read_data_row(&mut rdr)),
            b'E' => ErrorResponse { fields: try!(read_fields(&mut rdr)) },
            b'G' => {
                let format = try!(rdr.read_u8());
                let mut column_formats = vec![];
                for _ in 0..try!(rdr.read_u16::<BigEndian>()) {
                    column_formats.push(try!(rdr.read_u16::<BigEndian>()));
                }
                CopyInResponse {
                    format: format,
                    column_formats: column_formats,
                }
            }
            b'H' => {
                let format = try!(rdr.read_u8());
                let mut column_formats = vec![];
                for _ in 0..try!(rdr.read_u16::<BigEndian>()) {
                    column_formats.push(try!(rdr.read_u16::<BigEndian>()));
                }
                CopyOutResponse {
                    format: format,
                    column_formats: column_formats,
                }
            }
            b'I' => EmptyQueryResponse,
            b'K' => BackendKeyData {
                process_id: try!(rdr.read_u32::<BigEndian>()),
                secret_key: try!(rdr.read_u32::<BigEndian>())
            },
            b'n' => NoData,
            b'N' => NoticeResponse { fields: try!(read_fields(&mut rdr)) },
            b'R' => try!(read_auth_message(&mut rdr)),
            b's' => PortalSuspended,
            b'S' => ParameterStatus {
                parameter: try!(rdr.read_cstr()),
                value: try!(rdr.read_cstr())
            },
            b't' => try!(read_parameter_description(&mut rdr)),
            b'T' => try!(read_row_description(&mut rdr)),
            b'Z' => ReadyForQuery { _state: try!(rdr.read_u8()) },
            t => return Err(io::Error::new(io::ErrorKind::Other,
                                           format!("unexpected message tag `{}`", t))),
        };
        if rdr.limit() != 0 {
            return Err(io::Error::new(io::ErrorKind::Other, "didn't read entire message"));
        }
        Ok(ret)
    }
}

fn read_fields<R: BufRead>(buf: &mut R) -> io::Result<Vec<(u8, String)>> {
    let mut fields = vec![];
    loop {
        let ty = try!(buf.read_u8());
        if ty == 0 {
            break;
        }

        fields.push((ty, try!(buf.read_cstr())));
    }

    Ok(fields)
}

fn read_data_row<R: BufRead>(buf: &mut R) -> io::Result<BackendMessage> {
    let len = try!(buf.read_u16::<BigEndian>()) as usize;
    let mut values = Vec::with_capacity(len);

    for _ in 0..len {
        let val = match try!(buf.read_i32::<BigEndian>()) {
            -1 => None,
            len => {
                let mut data = vec![0; len as usize];
                try!(util::read_all(buf, &mut data));
                Some(data)
            }
        };
        values.push(val);
    }

    Ok(DataRow { row: values })
}

fn read_auth_message<R: Read>(buf: &mut R) -> io::Result<BackendMessage> {
    Ok(match try!(buf.read_i32::<BigEndian>()) {
        0 => AuthenticationOk,
        2 => AuthenticationKerberosV5,
        3 => AuthenticationCleartextPassword,
        5 => {
            let mut salt = [0; 4];
            try!(util::read_all(buf, &mut salt));
            AuthenticationMD5Password { salt: salt }
        },
        6 => AuthenticationSCMCredential,
        7 => AuthenticationGSS,
        9 => AuthenticationSSPI,
        t => return Err(io::Error::new(io::ErrorKind::Other,
                                       format!("unexpected authentication tag `{}`", t))),
    })
}

fn read_parameter_description<R: Read>(buf: &mut R) -> io::Result<BackendMessage> {
    let len = try!(buf.read_u16::<BigEndian>()) as usize;
    let mut types = Vec::with_capacity(len);

    for _ in 0..len {
        types.push(try!(buf.read_u32::<BigEndian>()));
    }

    Ok(ParameterDescription { types: types })
}

fn read_row_description<R: BufRead>(buf: &mut R) -> io::Result<BackendMessage> {
    let len = try!(buf.read_u16::<BigEndian>()) as usize;
    let mut types = Vec::with_capacity(len);

    for _ in 0..len {
        types.push(RowDescriptionEntry {
            name: try!(buf.read_cstr()),
            table_oid: try!(buf.read_u32::<BigEndian>()),
            column_id: try!(buf.read_i16::<BigEndian>()),
            type_oid: try!(buf.read_u32::<BigEndian>()),
            type_size: try!(buf.read_i16::<BigEndian>()),
            type_modifier: try!(buf.read_i32::<BigEndian>()),
            format: try!(buf.read_i16::<BigEndian>()),
        })
    }

    Ok(RowDescription { descriptions: types })
}

trait FromUsize {
    fn from_usize(x: usize) -> io::Result<Self>;
}

macro_rules! from_usize {
    ($t:ty) => {
        impl FromUsize for $t {
            fn from_usize(x: usize) -> io::Result<$t> {
                if x > <$t>::max_value() as usize {
                    Err(io::Error::new(io::ErrorKind::InvalidInput, "value too large to transmit"))
                } else {
                    Ok(x as $t)
                }
            }
        }
    }
}

from_usize!(u16);
from_usize!(i32);
<commit_msg>Inherit from Sized<commit_after>use std::io;
use std::io::prelude::*;
use std::mem;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};

use types::Oid;
use util;

use self::BackendMessage::*;
use self::FrontendMessage::*;

pub const PROTOCOL_VERSION: u32 = 0x0003_0000;
pub const CANCEL_CODE: u32 = 80877102;
pub const SSL_CODE: u32 = 80877103;

pub enum BackendMessage {
    AuthenticationCleartextPassword,
    AuthenticationGSS,
    AuthenticationKerberosV5,
    AuthenticationMD5Password {
        salt: [u8; 4]
    },
    AuthenticationOk,
    AuthenticationSCMCredential,
    AuthenticationSSPI,
    BackendKeyData {
        process_id: u32,
        secret_key: u32
    },
    BindComplete,
    CloseComplete,
    CommandComplete {
        tag: String,
    },
    // FIXME naming
    BCopyData {
        data: Vec<u8>,
    },
    BCopyDone,
    CopyInResponse {
        format: u8,
        column_formats: Vec<u16>,
    },
    CopyOutResponse {
        format: u8,
        column_formats: Vec<u16>,
    },
    DataRow {
        row: Vec<Option<Vec<u8>>>
    },
    EmptyQueryResponse,
    ErrorResponse {
        fields: Vec<(u8, String)>
    },
    NoData,
    NoticeResponse {
        fields: Vec<(u8, String)>
    },
    NotificationResponse {
        pid: u32,
        channel: String,
        payload: String,
    },
    ParameterDescription {
        types: Vec<Oid>
    },
    ParameterStatus {
        parameter: String,
        value: String,
    },
    ParseComplete,
    PortalSuspended,
    ReadyForQuery {
        _state: u8
    },
    RowDescription {
        descriptions: Vec<RowDescriptionEntry>
    }
}

pub struct RowDescriptionEntry {
    pub name: String,
    pub table_oid: Oid,
    pub column_id: i16,
    pub type_oid: Oid,
    pub type_size: i16,
    pub type_modifier: i32,
    pub format: i16
}

pub enum FrontendMessage<'a> {
    Bind {
        portal: &'a str,
        statement: &'a str,
        formats: &'a [i16],
        values: &'a [Option<Vec<u8>>],
        result_formats: &'a [i16]
    },
    CancelRequest {
        code: u32,
        process_id: u32,
        secret_key: u32,
    },
    Close {
        variant: u8,
        name: &'a str
    },
    CopyData {
        data: &'a [u8],
    },
    CopyDone,
    CopyFail {
        message: &'a str
    },
    Describe {
        variant: u8,
        name: &'a str
    },
    Execute {
        portal: &'a str,
        max_rows: i32
    },
    Parse {
        name: &'a str,
        query: &'a str,
        param_types: &'a [Oid]
    },
    PasswordMessage {
        password: &'a str
    },
    Query {
        query: &'a str
    },
    SslRequest {
        code: u32
    },
    StartupMessage {
        version: u32,
        parameters: &'a [(String, String)]
    },
    Sync,
    Terminate
}

#[doc(hidden)]
trait WriteCStr {
    fn write_cstr(&mut self, s: &str) -> io::Result<()>;
}

impl<W: Write> WriteCStr for W {
    fn write_cstr(&mut self, s: &str) -> io::Result<()> {
        try!(self.write_all(s.as_bytes()));
        Ok(try!(self.write_u8(0)))
    }
}

#[doc(hidden)]
pub trait WriteMessage {
    fn write_message(&mut self, &FrontendMessage) -> io::Result<()> ;
}

impl<W: Write> WriteMessage for W {
    fn write_message(&mut self, message: &FrontendMessage) -> io::Result<()> {
        let mut buf = vec![];
        let mut ident = None;

        match *message {
            Bind { portal, statement, formats, values, result_formats } => {
                ident = Some(b'B');
                try!(buf.write_cstr(portal));
                try!(buf.write_cstr(statement));

                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(formats.len()))));
                for &format in formats {
                    try!(buf.write_i16::<BigEndian>(format));
                }

                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(values.len()))));
                for value in values {
                    match *value {
                        None => try!(buf.write_i32::<BigEndian>(-1)),
                        Some(ref value) => {
                            try!(buf.write_i32::<BigEndian>(try!(i32::from_usize(value.len()))));
                            try!(buf.write_all(&**value));
                        }
                    }
                }

                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(result_formats.len()))));
                for &format in result_formats {
                    try!(buf.write_i16::<BigEndian>(format));
                }
            }
            CancelRequest { code, process_id, secret_key } => {
                try!(buf.write_u32::<BigEndian>(code));
                try!(buf.write_u32::<BigEndian>(process_id));
                try!(buf.write_u32::<BigEndian>(secret_key));
            }
            Close { variant, name } => {
                ident = Some(b'C');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            CopyData { data } => {
                ident = Some(b'd');
                try!(buf.write_all(data));
            }
            CopyDone => ident = Some(b'c'),
            CopyFail { message } => {
                ident = Some(b'f');
                try!(buf.write_cstr(message));
            }
            Describe { variant, name } => {
                ident = Some(b'D');
                try!(buf.write_u8(variant));
                try!(buf.write_cstr(name));
            }
            Execute { portal, max_rows } => {
                ident = Some(b'E');
                try!(buf.write_cstr(portal));
                try!(buf.write_i32::<BigEndian>(max_rows));
            }
            Parse { name, query, param_types } => {
                ident = Some(b'P');
                try!(buf.write_cstr(name));
                try!(buf.write_cstr(query));
                try!(buf.write_u16::<BigEndian>(try!(u16::from_usize(param_types.len()))));
                for &ty in param_types {
                    try!(buf.write_u32::<BigEndian>(ty));
                }
            }
            PasswordMessage { password } => {
                ident = Some(b'p');
                try!(buf.write_cstr(password));
            }
            Query { query } => {
                ident = Some(b'Q');
                try!(buf.write_cstr(query));
            }
            StartupMessage { version, parameters } => {
                try!(buf.write_u32::<BigEndian>(version));
                for &(ref k, ref v) in parameters {
                    try!(buf.write_cstr(&**k));
                    try!(buf.write_cstr(&**v));
                }
                try!(buf.write_u8(0));
            }
            SslRequest { code } => try!(buf.write_u32::<BigEndian>(code)),
            Sync => ident = Some(b'S'),
            Terminate => ident = Some(b'X'),
        }

        if let Some(ident) = ident {
            try!(self.write_u8(ident));
        }

        // add size of length value
        if buf.len() > u32::max_value() as usize - mem::size_of::<u32>() {
            return Err(io::Error::new(io::ErrorKind::InvalidInput, "value too large to transmit"));
        }
        try!(self.write_u32::<BigEndian>((buf.len() + mem::size_of::<u32>()) as u32));
        try!(self.write_all(&*buf));

        Ok(())
    }
}

#[doc(hidden)]
trait ReadCStr {
    fn read_cstr(&mut self) -> io::Result<String>;
}

impl<R: BufRead> ReadCStr for R {
    fn read_cstr(&mut self) -> io::Result<String> {
        let mut buf = vec![];
        try!(self.read_until(0, &mut buf));
        buf.pop();
        String::from_utf8(buf).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
    }
}

#[doc(hidden)]
pub trait ReadMessage {
    fn read_message(&mut self) -> io::Result<BackendMessage>;
}

impl<R: BufRead> ReadMessage for R {
    fn read_message(&mut self) -> io::Result<BackendMessage> {
        let ident = try!(self.read_u8());

        // subtract size of length value
        let len = try!(self.read_u32::<BigEndian>()) - mem::size_of::<u32>() as u32;
        let mut rdr = self.by_ref().take(len as u64);

        let ret = match ident {
            b'1' => ParseComplete,
            b'2' => BindComplete,
            b'3' => CloseComplete,
            b'A' => NotificationResponse {
                pid: try!(rdr.read_u32::<BigEndian>()),
                channel: try!(rdr.read_cstr()),
                payload: try!(rdr.read_cstr())
            },
            b'c' => BCopyDone,
            b'C' => CommandComplete { tag: try!(rdr.read_cstr()) },
            b'd' => {
                let mut data = vec![];
                try!(rdr.read_to_end(&mut data));
                BCopyData {
                    data: data,
                }
            }
            b'D' => try!(read_data_row(&mut rdr)),
            b'E' => ErrorResponse { fields: try!(read_fields(&mut rdr)) },
            b'G' => {
                let format = try!(rdr.read_u8());
                let mut column_formats = vec![];
                for _ in 0..try!(rdr.read_u16::<BigEndian>()) {
                    column_formats.push(try!(rdr.read_u16::<BigEndian>()));
                }
                CopyInResponse {
                    format: format,
                    column_formats: column_formats,
                }
            }
            b'H' => {
                let format = try!(rdr.read_u8());
                let mut column_formats = vec![];
                for _ in 0..try!(rdr.read_u16::<BigEndian>()) {
                    column_formats.push(try!(rdr.read_u16::<BigEndian>()));
                }
                CopyOutResponse {
                    format: format,
                    column_formats: column_formats,
                }
            }
            b'I' => EmptyQueryResponse,
            b'K' => BackendKeyData {
                process_id: try!(rdr.read_u32::<BigEndian>()),
                secret_key: try!(rdr.read_u32::<BigEndian>())
            },
            b'n' => NoData,
            b'N' => NoticeResponse { fields: try!(read_fields(&mut rdr)) },
            b'R' => try!(read_auth_message(&mut rdr)),
            b's' => PortalSuspended,
            b'S' => ParameterStatus {
                parameter: try!(rdr.read_cstr()),
                value: try!(rdr.read_cstr())
            },
            b't' => try!(read_parameter_description(&mut rdr)),
            b'T' => try!(read_row_description(&mut rdr)),
            b'Z' => ReadyForQuery { _state: try!(rdr.read_u8()) },
            t => return Err(io::Error::new(io::ErrorKind::Other,
                                           format!("unexpected message tag `{}`", t))),
        };
        if rdr.limit() != 0 {
            return Err(io::Error::new(io::ErrorKind::Other, "didn't read entire message"));
        }
        Ok(ret)
    }
}

fn read_fields<R: BufRead>(buf: &mut R) -> io::Result<Vec<(u8, String)>> {
    let mut fields = vec![];
    loop {
        let ty = try!(buf.read_u8());
        if ty == 0 {
            break;
        }

        fields.push((ty, try!(buf.read_cstr())));
    }

    Ok(fields)
}

fn read_data_row<R: BufRead>(buf: &mut R) -> io::Result<BackendMessage> {
    let len = try!(buf.read_u16::<BigEndian>()) as usize;
    let mut values = Vec::with_capacity(len);

    for _ in 0..len {
        let val = match try!(buf.read_i32::<BigEndian>()) {
            -1 => None,
            len => {
                let mut data = vec![0; len as usize];
                try!(util::read_all(buf, &mut data));
                Some(data)
            }
        };
        values.push(val);
    }

    Ok(DataRow { row: values })
}

fn read_auth_message<R: Read>(buf: &mut R) -> io::Result<BackendMessage> {
    Ok(match try!(buf.read_i32::<BigEndian>()) {
        0 => AuthenticationOk,
        2 => AuthenticationKerberosV5,
        3 => AuthenticationCleartextPassword,
        5 => {
            let mut salt = [0; 4];
            try!(util::read_all(buf, &mut salt));
            AuthenticationMD5Password { salt: salt }
        },
        6 => AuthenticationSCMCredential,
        7 => AuthenticationGSS,
        9 => AuthenticationSSPI,
        t => return Err(io::Error::new(io::ErrorKind::Other,
                                       format!("unexpected authentication tag `{}`", t))),
    })
}

fn read_parameter_description<R: Read>(buf: &mut R) -> io::Result<BackendMessage> {
    let len = try!(buf.read_u16::<BigEndian>()) as usize;
    let mut types = Vec::with_capacity(len);

    for _ in 0..len {
        types.push(try!(buf.read_u32::<BigEndian>()));
    }

    Ok(ParameterDescription { types: types })
}

fn read_row_description<R: BufRead>(buf: &mut R) -> io::Result<BackendMessage> {
    let len = try!(buf.read_u16::<BigEndian>()) as usize;
    let mut types = Vec::with_capacity(len);

    for _ in 0..len {
        types.push(RowDescriptionEntry {
            name: try!(buf.read_cstr()),
            table_oid: try!(buf.read_u32::<BigEndian>()),
            column_id: try!(buf.read_i16::<BigEndian>()),
            type_oid: try!(buf.read_u32::<BigEndian>()),
            type_size: try!(buf.read_i16::<BigEndian>()),
            type_modifier: try!(buf.read_i32::<BigEndian>()),
            format: try!(buf.read_i16::<BigEndian>()),
        })
    }

    Ok(RowDescription { descriptions: types })
}

trait FromUsize: Sized {
    fn from_usize(x: usize) -> io::Result<Self>;
}

macro_rules! from_usize {
    ($t:ty) => {
        impl FromUsize for $t {
            fn from_usize(x: usize) -> io::Result<$t> {
                if x > <$t>::max_value() as usize {
                    Err(io::Error::new(io::ErrorKind::InvalidInput, "value too large to transmit"))
                } else {
                    Ok(x as $t)
                }
            }
        }
    }
}

from_usize!(u16);
from_usize!(i32);
<|endoftext|>