<commit_before>//! Shared interrupt description and set-up code.
//! See the `bits*::irq` modules for arch-specific portions.

use bitflags::*;

use core::fmt;

/// x86 Exception description (see also Intel Vol. 3a Chapter 6).
#[derive(Debug)]
pub struct InterruptDescription {
    pub vector: u8,
    pub mnemonic: &'static str,
    pub description: &'static str,
    pub irqtype: &'static str,
    pub source: &'static str,
}

impl fmt::Display for InterruptDescription {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{} ({}, vec={}) {}",
            self.mnemonic, self.irqtype, self.vector, self.description
        )
    }
}

/// x86 External Interrupts (1-16).
pub static EXCEPTIONS: [InterruptDescription; 21] = [
    InterruptDescription {
        vector: 0,
        mnemonic: "#DE",
        description: "Divide Error",
        irqtype: "Fault",
        source: "DIV and IDIV instructions.",
    },
    InterruptDescription {
        vector: 1,
        mnemonic: "#DB",
        description: "Debug",
        irqtype: "Fault / Trap",
        source: "Debug condition",
    },
    InterruptDescription {
        vector: 2,
        mnemonic: "NMI",
        description: "Nonmaskable Interrupt",
        irqtype: "Interrupt",
        source: "Nonmaskable external interrupt.",
    },
    InterruptDescription {
        vector: 3,
        mnemonic: "#BP",
        description: "Breakpoint",
        irqtype: "Trap",
        source: "INT 3 instruction.",
    },
    InterruptDescription {
        vector: 4,
        mnemonic: "#OF",
        description: "Overflow",
        irqtype: "Trap",
        source: "INTO instruction.",
    },
    InterruptDescription {
        vector: 5,
        mnemonic: "#BR",
        description: "BOUND Range Exceeded",
        irqtype: "Fault",
        source: "BOUND instruction.",
    },
    InterruptDescription {
        vector: 6,
        mnemonic: "#UD",
        description: "Invalid Opcode (Undefined \
                      Opcode)",
        irqtype: "Fault",
        source: "UD2 instruction or reserved \
                 opcode.",
    },
    InterruptDescription {
        vector: 7,
        mnemonic: "#NM",
        description: "Device Not Available (No \
                      Math Coprocessor)",
        irqtype: "Fault",
        source: "Floating-point or WAIT/FWAIT \
                 instruction.",
    },
    InterruptDescription {
        vector: 8,
        mnemonic: "#DF",
        description: "Double Fault",
        irqtype: "Abort",
        source: "Any instruction that can \
                 generate an exception, an NMI, \
                 or an INTR.",
    },
    InterruptDescription {
        vector: 9,
        mnemonic: "",
        description: "Coprocessor Segment Overrun",
        irqtype: "Fault",
        source: "Floating-point instruction.",
    },
    InterruptDescription {
        vector: 10,
        mnemonic: "#TS",
        description: "Invalid TSS",
        irqtype: "Fault",
        source: "Task switch or TSS access.",
    },
    InterruptDescription {
        vector: 11,
        mnemonic: "#NP",
        description: "Segment Not Present",
        irqtype: "Fault",
        source: "Loading segment registers or \
                 accessing system segments.",
    },
    InterruptDescription {
        vector: 12,
        mnemonic: "#SS",
        description: "Stack-Segment Fault",
        irqtype: "Fault",
        source: "Stack operations and SS register \
                 loads.",
    },
    InterruptDescription {
        vector: 13,
        mnemonic: "#GP",
        description: "General Protection",
        irqtype: "Fault",
        source: "Any memory reference and other \
                 protection checks.",
    },
    InterruptDescription {
        vector: 14,
        mnemonic: "#PF",
        description: "Page Fault",
        irqtype: "Fault",
        source: "Any memory reference.",
    },
    InterruptDescription {
        vector: 15,
        mnemonic: "",
        description: "RESERVED",
        irqtype: "",
        source: "None.",
    },
    InterruptDescription {
        vector: 16,
        mnemonic: "#MF",
        description: "x87 FPU Floating-Point",
        irqtype: "Fault",
        source: "x87 FPU instructions.",
    },
    InterruptDescription {
        vector: 17,
        mnemonic: "#AC",
        description: "Alignment Check",
        irqtype: "Fault",
        source: "Unaligned memory access.",
    },
    InterruptDescription {
        vector: 18,
        mnemonic: "#MC",
        description: "Machine Check",
        irqtype: "Abort",
        source: "Internal machine error.",
    },
    InterruptDescription {
        vector: 19,
        mnemonic: "#XM",
        description: "SIMD Floating-Point",
        irqtype: "Fault",
        source: "SSE SIMD instructions.",
    },
    InterruptDescription {
        vector: 20,
        mnemonic: "#VE",
        description: "Virtualization",
        irqtype: "Fault",
        source: "EPT violation.",
    },
];

bitflags! {
    // Taken from Intel Manual Section 4.7 Page-Fault Exceptions.
    pub struct PageFaultError: u32 {
        /// 0: The fault was caused by a non-present page.
        /// 1: The fault was caused by a page-level protection violation
        const P = bit!(0);

        /// 0: The access causing the fault was a read.
        /// 1: The access causing the fault was a write.
        const WR = bit!(1);

        /// 0: The access causing the fault originated when the processor
        /// was executing in supervisor mode.
        /// 1: The access causing the fault originated when the processor
        /// was executing in user mode.
        const US = bit!(2);

        /// 0: The fault was not caused by reserved bit violation.
        /// 1: The fault was caused by reserved bits set to 1 in a page directory.
        const RSVD = bit!(3);

        /// 0: The fault was not caused by an instruction fetch.
        /// 1: The fault was caused by an instruction fetch.
        const ID = bit!(4);

        /// 0: The fault was not by protection keys.
        /// 1: There was a protection key violation.
        const PK = bit!(5);
    }
}

impl fmt::Display for PageFaultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let p = match self.contains(PageFaultError::P) {
            false => "The fault was caused by a non-present page.",
            true => "The fault was caused by a page-level protection violation.",
        };
        let wr = match self.contains(PageFaultError::WR) {
            false => "The access causing the fault was a read.",
            true => "The access causing the fault was a write.",
        };
        let us = match self.contains(PageFaultError::US) {
            false => {
                "The access causing the fault originated when the processor was executing in \
                 supervisor mode."
            }
            true => {
                "The access causing the fault originated when the processor was executing in user \
                 mode."
            }
        };
        let rsvd = match self.contains(PageFaultError::RSVD) {
            false => "The fault was not caused by reserved bit violation.",
            true => "The fault was caused by reserved bits set to 1 in a page directory.",
        };
        let id = match self.contains(PageFaultError::ID) {
            false => "The fault was not caused by an instruction fetch.",
            true => "The fault was caused by an instruction fetch.",
        };

        write!(f, "{}\n{}\n{}\n{}\n{}", p, wr, us, rsvd, id)
    }
}

/// Enable Interrupts.
pub unsafe fn enable() {
    asm!("sti");
}

/// Disable Interrupts.
pub unsafe fn disable() {
    asm!("cli");
}

#[cfg(all(test, feature = "utest"))]
mod test {
    use super::*;
    #[test]
    fn bit_macro() {
        assert!(PageFaultError::PK.bits() == 0b100000);
        assert!(PageFaultError::ID.bits() == 0b10000);
        assert!(PageFaultError::RSVD.bits() == 0b1000);
        assert!(PageFaultError::US.bits() == 0b100);
        assert!(PageFaultError::WR.bits() == 0b10);
        assert!(PageFaultError::P.bits() == 0b1);
    }

    /// Generate a software interrupt.
    /// This is a macro argument needs to be an immediate.
    #[macro_export]
    macro_rules! int {
    ($x:expr) => {{
        asm!("int $0" :: "N" ($x));
    }};
}
}
<commit_msg>Bring back accidentially moved code.<commit_after>//! Shared interrupt description and set-up code.
//! See the `bits*::irq` modules for arch-specific portions.

use bitflags::*;

use core::fmt;

/// x86 Exception description (see also Intel Vol. 3a Chapter 6).
#[derive(Debug)]
pub struct InterruptDescription {
    pub vector: u8,
    pub mnemonic: &'static str,
    pub description: &'static str,
    pub irqtype: &'static str,
    pub source: &'static str,
}

impl fmt::Display for InterruptDescription {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{} ({}, vec={}) {}",
            self.mnemonic, self.irqtype, self.vector, self.description
        )
    }
}

/// x86 External Interrupts (1-16).
pub static EXCEPTIONS: [InterruptDescription; 21] = [
    InterruptDescription {
        vector: 0,
        mnemonic: "#DE",
        description: "Divide Error",
        irqtype: "Fault",
        source: "DIV and IDIV instructions.",
    },
    InterruptDescription {
        vector: 1,
        mnemonic: "#DB",
        description: "Debug",
        irqtype: "Fault / Trap",
        source: "Debug condition",
    },
    InterruptDescription {
        vector: 2,
        mnemonic: "NMI",
        description: "Nonmaskable Interrupt",
        irqtype: "Interrupt",
        source: "Nonmaskable external interrupt.",
    },
    InterruptDescription {
        vector: 3,
        mnemonic: "#BP",
        description: "Breakpoint",
        irqtype: "Trap",
        source: "INT 3 instruction.",
    },
    InterruptDescription {
        vector: 4,
        mnemonic: "#OF",
        description: "Overflow",
        irqtype: "Trap",
        source: "INTO instruction.",
    },
    InterruptDescription {
        vector: 5,
        mnemonic: "#BR",
        description: "BOUND Range Exceeded",
        irqtype: "Fault",
        source: "BOUND instruction.",
    },
    InterruptDescription {
        vector: 6,
        mnemonic: "#UD",
        description: "Invalid Opcode (Undefined \
                      Opcode)",
        irqtype: "Fault",
        source: "UD2 instruction or reserved \
                 opcode.",
    },
    InterruptDescription {
        vector: 7,
        mnemonic: "#NM",
        description: "Device Not Available (No \
                      Math Coprocessor)",
        irqtype: "Fault",
        source: "Floating-point or WAIT/FWAIT \
                 instruction.",
    },
    InterruptDescription {
        vector: 8,
        mnemonic: "#DF",
        description: "Double Fault",
        irqtype: "Abort",
        source: "Any instruction that can \
                 generate an exception, an NMI, \
                 or an INTR.",
    },
    InterruptDescription {
        vector: 9,
        mnemonic: "",
        description: "Coprocessor Segment Overrun",
        irqtype: "Fault",
        source: "Floating-point instruction.",
    },
    InterruptDescription {
        vector: 10,
        mnemonic: "#TS",
        description: "Invalid TSS",
        irqtype: "Fault",
        source: "Task switch or TSS access.",
    },
    InterruptDescription {
        vector: 11,
        mnemonic: "#NP",
        description: "Segment Not Present",
        irqtype: "Fault",
        source: "Loading segment registers or \
                 accessing system segments.",
    },
    InterruptDescription {
        vector: 12,
        mnemonic: "#SS",
        description: "Stack-Segment Fault",
        irqtype: "Fault",
        source: "Stack operations and SS register \
                 loads.",
    },
    InterruptDescription {
        vector: 13,
        mnemonic: "#GP",
        description: "General Protection",
        irqtype: "Fault",
        source: "Any memory reference and other \
                 protection checks.",
    },
    InterruptDescription {
        vector: 14,
        mnemonic: "#PF",
        description: "Page Fault",
        irqtype: "Fault",
        source: "Any memory reference.",
    },
    InterruptDescription {
        vector: 15,
        mnemonic: "",
        description: "RESERVED",
        irqtype: "",
        source: "None.",
    },
    InterruptDescription {
        vector: 16,
        mnemonic: "#MF",
        description: "x87 FPU Floating-Point",
        irqtype: "Fault",
        source: "x87 FPU instructions.",
    },
    InterruptDescription {
        vector: 17,
        mnemonic: "#AC",
        description: "Alignment Check",
        irqtype: "Fault",
        source: "Unaligned memory access.",
    },
    InterruptDescription {
        vector: 18,
        mnemonic: "#MC",
        description: "Machine Check",
        irqtype: "Abort",
        source: "Internal machine error.",
    },
    InterruptDescription {
        vector: 19,
        mnemonic: "#XM",
        description: "SIMD Floating-Point",
        irqtype: "Fault",
        source: "SSE SIMD instructions.",
    },
    InterruptDescription {
        vector: 20,
        mnemonic: "#VE",
        description: "Virtualization",
        irqtype: "Fault",
        source: "EPT violation.",
    },
];

bitflags! {
    // Taken from Intel Manual Section 4.7 Page-Fault Exceptions.
    pub struct PageFaultError: u32 {
        /// 0: The fault was caused by a non-present page.
        /// 1: The fault was caused by a page-level protection violation
        const P = bit!(0);

        /// 0: The access causing the fault was a read.
        /// 1: The access causing the fault was a write.
        const WR = bit!(1);

        /// 0: The access causing the fault originated when the processor
        /// was executing in supervisor mode.
        /// 1: The access causing the fault originated when the processor
        /// was executing in user mode.
        const US = bit!(2);

        /// 0: The fault was not caused by reserved bit violation.
        /// 1: The fault was caused by reserved bits set to 1 in a page directory.
        const RSVD = bit!(3);

        /// 0: The fault was not caused by an instruction fetch.
        /// 1: The fault was caused by an instruction fetch.
        const ID = bit!(4);

        /// 0: The fault was not by protection keys.
        /// 1: There was a protection key violation.
        const PK = bit!(5);
    }
}

impl fmt::Display for PageFaultError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let p = match self.contains(PageFaultError::P) {
            false => "The fault was caused by a non-present page.",
            true => "The fault was caused by a page-level protection violation.",
        };
        let wr = match self.contains(PageFaultError::WR) {
            false => "The access causing the fault was a read.",
            true => "The access causing the fault was a write.",
        };
        let us = match self.contains(PageFaultError::US) {
            false => {
                "The access causing the fault originated when the processor was executing in \
                 supervisor mode."
            }
            true => {
                "The access causing the fault originated when the processor was executing in user \
                 mode."
            }
        };
        let rsvd = match self.contains(PageFaultError::RSVD) {
            false => "The fault was not caused by reserved bit violation.",
            true => "The fault was caused by reserved bits set to 1 in a page directory.",
        };
        let id = match self.contains(PageFaultError::ID) {
            false => "The fault was not caused by an instruction fetch.",
            true => "The fault was caused by an instruction fetch.",
        };

        write!(f, "{}\n{}\n{}\n{}\n{}", p, wr, us, rsvd, id)
    }
}

/// Enable Interrupts.
pub unsafe fn enable() {
    asm!("sti");
}

/// Disable Interrupts.
pub unsafe fn disable() {
    asm!("cli");
}
/// Generate a software interrupt.
/// This is a macro argument needs to be an immediate.
#[macro_export]
macro_rules! int {
    ($x:expr) => {{
        asm!("int $0" :: "N" ($x));
    }};
}

#[cfg(all(test, feature = "utest"))]
mod test {
    use super::*;
    #[test]
    fn bit_macro() {
        assert!(PageFaultError::PK.bits() == 0b100000);
        assert!(PageFaultError::ID.bits() == 0b10000);
        assert!(PageFaultError::RSVD.bits() == 0b1000);
        assert!(PageFaultError::US.bits() == 0b100);
        assert!(PageFaultError::WR.bits() == 0b10);
        assert!(PageFaultError::P.bits() == 0b1);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

#![feature(libc)]
#![feature(asm)]
#![feature(alloc)]
#![feature(std_misc)]
#![feature(collections)]
#![feature(collections_drain)]
#![feature(link_llvm_intrinsics)]

//! This is an implementation of the `RCImmixCons` garbage collector.
//!
//! A conservative reference counting garbage collector with the immix heap
//! partition schema. For details please refer to:
//!
//! - S. M. S. Blackburn and K. K. S. McKinley. Immix: a mark-region garbage
//!   collector with space efficiency, fast collection, and mutator performance.
//!   ACM SIGPLAN Notices, 43(6):22, May 2008.
//! - R. Shahriyar, S. M. Blackburn, and D. Frampton. Down for the count?  Getting
//!   reference counting back in the ring. ACM SIGPLAN Notices, 47(11):73, Jan.
//!   2013.
//! - R. Shahriyar, S. M. Blackburn, and K. S. McKinley. Fast conservative garbage
//!   collection. In Proceedings of the 2014 ACM International Conference on
//!   Object Oriented Programming Systems Languages & Applications - OOPSLA ’14,
//!   pages 121-139, New York, New York, USA, Oct. 2014. ACM Press.
//! - R. Shahriyar, S. M. Blackburn, X. Yang, and K. S. McKinley. Taking off the
//!   gloves with reference counting Immix. ACM SIGPLAN Notices, 48(10):93-110,
//!   Nov. 2013.
//!
//! To use this garbage collector your objects must be structs derived from
//! `GCObject`. Allocation and collection is done using `RCImmixCons`.

extern crate libc;
use std::ptr;

pub use self::gc_object::{GCHeader, GCRTTI, GCObject, GCObjectRef};

mod macros;
mod constants;
mod gc_object;
mod spaces;
mod stack;

/// The `RCImmixCons` garbage collector.
///
/// The `allocate()` function will return a pointer to a `GCObject`. Please
/// see the documentation of `GCHeader`, `GCRTTI` and `GCObject` for details.
///
/// Always call `write_barrier()` on an object before modifying its members.
pub struct RCImmixCons {
    /// The different spaces of this garbage collector.
    spaces: spaces::Spaces,
}

impl RCImmixCons {
    /// Create a new `RCImmixCons`.
    pub fn new() -> RCImmixCons {
        return RCImmixCons {
            spaces: spaces::Spaces::new(),
        };
    }

    /// Allocate a new object described by the `rtti` or returns `None`.
    ///
    /// This may trigger a garbage collection if the allocation was not
    /// succussful. If there is still no memory to fullfill the allocation
    /// request return `None`.
    pub fn allocate(&mut self, rtti: *const GCRTTI) -> Option<GCObjectRef> {
        return self.spaces.allocate(rtti)
                   .or_else(|| { self.collect(true, true);
                                 self.spaces.allocate(rtti) });
    }

    /// Trigger a garbage collection.
    ///
    /// This will always run the referece counting collector. If `evacuation`
    /// is set the collectors will try to evacuate. If `cycle_collect` is set
    /// the immix tracing collector will be used.
    #[allow(unused_variables)]
    pub fn collect(&mut self, evacuation: bool, cycle_collect: bool) {
        // Calling this function befor **ANYTHING** is important to save the
        // callee save registers on the stack.
        let registers = stack::Stack::get_registers();
        return self.spaces.collect(evacuation, cycle_collect);
    }

    /// Set an address to an object reference as static root.
    ///
    /// Use this to mark global/static variables as roots. This is needed, if
    /// the pointer to a garbage collected object does not reside on the stack
    /// or in any register.
    pub fn set_static_root(&mut self, address: *const GCObjectRef) {
        self.spaces.set_static_root(address);
    }

    /// A write barrier for the given `object`.
    ///
    /// Call this function before modifying the members of this object!
    pub fn write_barrier(&mut self, object: GCObjectRef) {
        if self.spaces.write_barrier(object) {
            self.collect(false, false);
        }
    }
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_create() -> *mut RCImmixCons {
    return unsafe { std::boxed::into_raw(Box::new(RCImmixCons::new())) };
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_allocate(this: *mut RCImmixCons, rtti: *const GCRTTI)
    -> GCObjectRef {
    unsafe { return (*this).allocate(rtti).unwrap_or(ptr::null_mut()); }
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_collect(this: *mut RCImmixCons, evacuation: bool, cycle_collect: bool) {
    unsafe { (*this).collect(evacuation, cycle_collect) };
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_set_static_root(this: *mut RCImmixCons, address: *const GCObjectRef) {
    unsafe { (*this).set_static_root(address) };
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_write_barrier(this: *mut RCImmixCons, object: GCObjectRef) {
    unsafe { (*this).write_barrier(object) };
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_destroy(this: *mut RCImmixCons) {
    let _to_be_dropped = unsafe{ Box::from_raw(this) };
}
<commit_msg>remove unnecessary `unsafe` block in lib.rs<commit_after>// Copyright (c) <2015> <lummax>
// Licensed under MIT (http://opensource.org/licenses/MIT)

#![feature(libc)]
#![feature(asm)]
#![feature(alloc)]
#![feature(std_misc)]
#![feature(collections)]
#![feature(collections_drain)]
#![feature(link_llvm_intrinsics)]

//! This is an implementation of the `RCImmixCons` garbage collector.
//!
//! A conservative reference counting garbage collector with the immix heap
//! partition schema. For details please refer to:
//!
//! - S. M. S. Blackburn and K. K. S. McKinley. Immix: a mark-region garbage
//!   collector with space efficiency, fast collection, and mutator performance.
//!   ACM SIGPLAN Notices, 43(6):22, May 2008.
//! - R. Shahriyar, S. M. Blackburn, and D. Frampton. Down for the count?  Getting
//!   reference counting back in the ring. ACM SIGPLAN Notices, 47(11):73, Jan.
//!   2013.
//! - R. Shahriyar, S. M. Blackburn, and K. S. McKinley. Fast conservative garbage
//!   collection. In Proceedings of the 2014 ACM International Conference on
//!   Object Oriented Programming Systems Languages & Applications - OOPSLA ’14,
//!   pages 121-139, New York, New York, USA, Oct. 2014. ACM Press.
//! - R. Shahriyar, S. M. Blackburn, X. Yang, and K. S. McKinley. Taking off the
//!   gloves with reference counting Immix. ACM SIGPLAN Notices, 48(10):93-110,
//!   Nov. 2013.
//!
//! To use this garbage collector your objects must be structs derived from
//! `GCObject`. Allocation and collection is done using `RCImmixCons`.

extern crate libc;
use std::ptr;

pub use self::gc_object::{GCHeader, GCRTTI, GCObject, GCObjectRef};

mod macros;
mod constants;
mod gc_object;
mod spaces;
mod stack;

/// The `RCImmixCons` garbage collector.
///
/// The `allocate()` function will return a pointer to a `GCObject`. Please
/// see the documentation of `GCHeader`, `GCRTTI` and `GCObject` for details.
///
/// Always call `write_barrier()` on an object before modifying its members.
pub struct RCImmixCons {
    /// The different spaces of this garbage collector.
    spaces: spaces::Spaces,
}

impl RCImmixCons {
    /// Create a new `RCImmixCons`.
    pub fn new() -> RCImmixCons {
        return RCImmixCons {
            spaces: spaces::Spaces::new(),
        };
    }

    /// Allocate a new object described by the `rtti` or returns `None`.
    ///
    /// This may trigger a garbage collection if the allocation was not
    /// succussful. If there is still no memory to fullfill the allocation
    /// request return `None`.
    pub fn allocate(&mut self, rtti: *const GCRTTI) -> Option<GCObjectRef> {
        return self.spaces.allocate(rtti)
                   .or_else(|| { self.collect(true, true);
                                 self.spaces.allocate(rtti) });
    }

    /// Trigger a garbage collection.
    ///
    /// This will always run the referece counting collector. If `evacuation`
    /// is set the collectors will try to evacuate. If `cycle_collect` is set
    /// the immix tracing collector will be used.
    #[allow(unused_variables)]
    pub fn collect(&mut self, evacuation: bool, cycle_collect: bool) {
        // Calling this function befor **ANYTHING** is important to save the
        // callee save registers on the stack.
        let registers = stack::Stack::get_registers();
        return self.spaces.collect(evacuation, cycle_collect);
    }

    /// Set an address to an object reference as static root.
    ///
    /// Use this to mark global/static variables as roots. This is needed, if
    /// the pointer to a garbage collected object does not reside on the stack
    /// or in any register.
    pub fn set_static_root(&mut self, address: *const GCObjectRef) {
        self.spaces.set_static_root(address);
    }

    /// A write barrier for the given `object`.
    ///
    /// Call this function before modifying the members of this object!
    pub fn write_barrier(&mut self, object: GCObjectRef) {
        if self.spaces.write_barrier(object) {
            self.collect(false, false);
        }
    }
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_create() -> *mut RCImmixCons {
    return std::boxed::into_raw(Box::new(RCImmixCons::new()));
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_allocate(this: *mut RCImmixCons, rtti: *const GCRTTI)
    -> GCObjectRef {
    unsafe { return (*this).allocate(rtti).unwrap_or(ptr::null_mut()); }
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_collect(this: *mut RCImmixCons, evacuation: bool, cycle_collect: bool) {
    unsafe { (*this).collect(evacuation, cycle_collect) };
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_set_static_root(this: *mut RCImmixCons, address: *const GCObjectRef) {
    unsafe { (*this).set_static_root(address) };
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_write_barrier(this: *mut RCImmixCons, object: GCObjectRef) {
    unsafe { (*this).write_barrier(object) };
}

#[no_mangle]
#[doc(hidden)]
pub extern fn rcx_destroy(this: *mut RCImmixCons) {
    let _to_be_dropped = unsafe{ Box::from_raw(this) };
}
<|endoftext|>


<commit_before>//! DaZeus IRC bot bindings for rust
#![feature(io, std_misc, net, collections)]

extern crate "rustc-serialize" as serialize;
extern crate unix_socket;

pub use self::event::*;
pub use self::response::*;
pub use self::request::*;
pub use self::scope::*;
use self::handlers::{Reader, Writer, Handler};
use self::util::Connection;
use std::collections::HashMap;
use std::io::{Read, Write, BufStream};
use std::sync::Future;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread;

mod event;
mod handlers;
mod request;
mod response;
mod scope;
pub mod error;
pub mod util;

/// Methods that need to be implemented for sending commands to the server
pub trait Commander {
    fn subscribe(&mut self, event: &str, callback: &Fn(Event)) -> Future<Response>;
    fn subscribe_command(&mut self, command: &str, callback: &Fn(Event)) -> Future<Response>;
    fn networks(&self) -> Future<Response>;
    fn channels(&self, network: &str) -> Future<Response>;
    fn message(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn notice(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn ctcp(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn ctcp_reply(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn action(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn reply(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn send_names(&self, network: &str, channel: &str) -> Future<Response>;
    fn names(&self, network: &str, channel: &str) -> Future<Event>;
    fn send_whois(&self, network: &str, nick: &str) -> Future<Response>;
    fn whois(&self, network: &str, nick: &str) -> Future<Event>;
    fn join(&self, network: &str, channel: &str) -> Future<Response>;
    fn part(&self, network: &str, channel: &str) -> Future<Response>;
    fn nick(&self, network: &str) -> Future<Response>;
    fn handshake(&self, name: &str, version: &str, config: Option<&str>) -> Future<Response>;
    fn get_config(&self, group: &str, name: &str) -> Future<Response>;
    fn get_property(&self, name: &str, scope: Scope) -> Future<Response>;
    fn set_property(&self, name: &str, value: &str, scope: Scope) -> Future<Response>;
    fn unset_property(&self, name: &str, name: &str, scope: Scope) -> Future<Response>;
    fn get_property_keys(&self, prefix: &str, scope: Scope) -> Future<Response>;
    fn set_permission(&self, permission: &str, allow: bool, scope: Scope) -> Future<Response>;
    fn has_permission(&self, permission: &str, default: bool, scope: Scope) -> Future<Response>;
    fn unset_permission(&self, permission: &str, scope: Scope) -> Future<Response>;
}

/// The base DaZeus struct
pub struct DaZeus<'a> {
    event_rx: Receiver<Event>,
    request_tx: Sender<(Request, Sender<Response>)>,
    listeners: HashMap<EventType, Vec<&'a Fn(Event)>>,
}

impl<'a> DaZeus<'a> {
    /// Create a new instance of DaZeus from the given connection
    pub fn from_conn(conn: Connection) -> DaZeus<'a> {
        let clone = conn.try_clone().unwrap();
        DaZeus::new(conn, clone)
    }

    /// Create a new instance of DaZeus from the given connection, making use of a buffered stream
    pub fn from_conn_buff(conn: Connection) -> DaZeus<'a> {
        let clone = conn.try_clone().unwrap();
        DaZeus::new(BufStream::new(conn), BufStream::new(clone))
    }

    /// Create a new instance from a Read and Send, note that both need to point to the same socket
    pub fn new<R: Read + Send + 'static, W: Write + Send + 'static>(read: R, write: W) -> DaZeus<'a> {
        let (read_tx, read_rx) = channel();
        let (write_tx, write_rx) = channel();
        let mut reader = Reader::new(read, read_tx);
        let mut writer = Writer::new(write, write_rx);

        let (event_tx, event_rx) = channel();
        let (request_tx, request_rx) = channel();
        let mut handler = Handler::new(write_tx, event_tx);

        thread::spawn(move || { reader.run(); });
        thread::spawn(move || { writer.run(); });
        thread::spawn(move || { handler.run(read_rx, request_rx); });

        DaZeus { event_rx: event_rx, request_tx: request_tx, listeners: HashMap::new() }
    }

    /// Send a new Json packet to DaZeus
    pub fn send(&self, data: Request) -> Future<Response> {
        let (response_tx, response_rx) = channel();
        match self.request_tx.send((data, response_tx)) {
            Err(e) => panic!(e),
            Ok(_) => (),
        }
        Future::from_receiver(response_rx)
    }

    /// Handle an event received
    fn handle_event(&self, event: Event) {
        if self.listeners.contains_key(&event.event) {
            let listeners = self.listeners.get(&event.event).unwrap();
            for listener in listeners {
                (**listener)(event.clone());
            }
        }
    }

    /// Loop wait for messages to receive in a blocking way
    pub fn listen(&self) {
        loop {
            match self.event_rx.recv() {
                Ok(event) => self.handle_event(event),
                Err(err) => panic!(err)
            }
        }
    }

    /// Subscribe to an event type and call the callback function every time such an event occurs
    pub fn subscribe(&mut self, event: EventType, callback: &'a Fn(Event)) -> Future<Response> {
        let request = match event {
            EventType::Command(ref cmd) => Request::SubscribeCommand(cmd.clone(), None),
            _ => Request::Subscribe(event.to_string()),
        };

        if !self.listeners.contains_key(&event) {
            self.listeners.insert(event.clone(), Vec::new());
        }

        // borrow listeners only for adding the listener callback
        {
            let mut listeners = self.listeners.get_mut(&event).unwrap();
            listeners.push(callback);
        }

        // because we can't have a mutable borrow when borrowing immutable over here
        self.send(request)
    }

    /// Subscribe to a command and call the callback function every time such a command occurs
    pub fn subscribe_command(&mut self, command: &str, callback: &'a Fn(Event)) -> Future<Response> {
        self.subscribe(EventType::Command(String::from_str(command)), callback)
    }
}
<commit_msg>Implemented more methods for sending requests, better callbacks<commit_after>//! DaZeus IRC bot bindings for rust
#![feature(io, std_misc, net, collections)]

extern crate "rustc-serialize" as serialize;
extern crate unix_socket;

pub use self::event::*;
pub use self::response::*;
pub use self::request::*;
pub use self::scope::*;
use self::handlers::{Reader, Writer, Handler};
use self::util::Connection;
use std::collections::HashMap;
use std::io::{Read, Write, BufStream};
use std::sync::Future;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread;

mod event;
mod handlers;
mod request;
mod response;
mod scope;
pub mod error;
pub mod util;

/// Methods that need to be implemented for sending commands to the server
pub trait Commander {
    fn subscribe(&mut self, event: &str, callback: &Fn(Event)) -> Future<Response>;
    fn subscribe_command(&mut self, command: &str, callback: &Fn(Event)) -> Future<Response>;
    fn networks(&self) -> Future<Response>;
    fn channels(&self, network: &str) -> Future<Response>;
    fn message(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn notice(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn ctcp(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn ctcp_reply(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn action(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn reply(&self, network: &str, channel: &str, message: &str) -> Future<Response>;
    fn send_names(&self, network: &str, channel: &str) -> Future<Response>;
    fn names(&self, network: &str, channel: &str) -> Future<Event>;
    fn send_whois(&self, network: &str, nick: &str) -> Future<Response>;
    fn whois(&self, network: &str, nick: &str) -> Future<Event>;
    fn join(&self, network: &str, channel: &str) -> Future<Response>;
    fn part(&self, network: &str, channel: &str) -> Future<Response>;
    fn nick(&self, network: &str) -> Future<Response>;
    fn handshake(&self, name: &str, version: &str, config: Option<&str>) -> Future<Response>;
    fn get_config(&self, group: &str, name: &str) -> Future<Response>;
    fn get_property(&self, name: &str, scope: Scope) -> Future<Response>;
    fn set_property(&self, name: &str, value: &str, scope: Scope) -> Future<Response>;
    fn unset_property(&self, name: &str, scope: Scope) -> Future<Response>;
    fn get_property_keys(&self, prefix: &str, scope: Scope) -> Future<Response>;
    fn set_permission(&self, permission: &str, allow: bool, scope: Scope) -> Future<Response>;
    fn has_permission(&self, permission: &str, default: bool, scope: Scope) -> Future<Response>;
    fn unset_permission(&self, permission: &str, scope: Scope) -> Future<Response>;
}

/// The base DaZeus struct
pub struct DaZeus<'a> {
    event_rx: Receiver<Event>,
    request_tx: Sender<(Request, Sender<Response>)>,
    listeners: HashMap<EventType, Vec<Box<Fn(Event) + 'a>>>,
}

impl<'a> DaZeus<'a> {
    /// Create a new instance of DaZeus from the given connection
    pub fn from_conn(conn: Connection) -> DaZeus<'a> {
        let clone = conn.try_clone().unwrap();
        DaZeus::new(conn, clone)
    }

    /// Create a new instance of DaZeus from the given connection, making use of a buffered stream
    pub fn from_conn_buff(conn: Connection) -> DaZeus<'a> {
        let clone = conn.try_clone().unwrap();
        DaZeus::new(BufStream::new(conn), BufStream::new(clone))
    }

    /// Create a new instance from a Read and Send, note that both need to point to the same socket
    pub fn new<R: Read + Send + 'static, W: Write + Send + 'static>(read: R, write: W) -> DaZeus<'a> {
        let (read_tx, read_rx) = channel();
        let (write_tx, write_rx) = channel();
        let mut reader = Reader::new(read, read_tx);
        let mut writer = Writer::new(write, write_rx);

        let (event_tx, event_rx) = channel();
        let (request_tx, request_rx) = channel();
        let mut handler = Handler::new(write_tx, event_tx);

        thread::spawn(move || { reader.run(); });
        thread::spawn(move || { writer.run(); });
        thread::spawn(move || { handler.run(read_rx, request_rx); });

        DaZeus { event_rx: event_rx, request_tx: request_tx, listeners: HashMap::new() }
    }

    /// Send a new Json packet to DaZeus
    pub fn send(&self, data: Request) -> Future<Response> {
        let (response_tx, response_rx) = channel();
        match self.request_tx.send((data, response_tx)) {
            Err(e) => panic!(e),
            Ok(_) => (),
        }
        Future::from_receiver(response_rx)
    }

    /// Handle an event received
    fn handle_event(&self, event: Event) {
        if self.listeners.contains_key(&event.event) {
            let listeners = self.listeners.get(&event.event).unwrap();
            for listener in listeners {
                (**listener)(event.clone());
            }
        }
    }

    /// Loop wait for messages to receive in a blocking way
    pub fn listen(&self) {
        loop {
            match self.event_rx.recv() {
                Ok(event) => self.handle_event(event),
                Err(err) => panic!(err)
            }
        }
    }

    /// Subscribe to an event type and call the callback function every time such an event occurs
    pub fn subscribe<F>(&mut self, event: EventType, callback: F) -> Future<Response>
        where F: Fn(Event) + 'a
    {
        let request = match event {
            EventType::Command(ref cmd) => Request::SubscribeCommand(cmd.clone(), None),
            _ => Request::Subscribe(event.to_string()),
        };

        if !self.listeners.contains_key(&event) {
            self.listeners.insert(event.clone(), Vec::new());
        }

        // borrow listeners only for adding the listener callback
        {
            let mut listeners = self.listeners.get_mut(&event).unwrap();
            listeners.push(Box::new(callback));
        }

        // because we can't have a mutable borrow when borrowing immutable over here
        self.send(request)
    }

    /// Subscribe to a command and call the callback function every time such a command occurs
    pub fn subscribe_command<F>(&mut self, command: &str, callback: F) -> Future<Response>
        where F: Fn(Event) + 'a
    {
        self.subscribe(EventType::Command(String::from_str(command)), callback)
    }

    /// Retrieve the networks the bot is connected to
    pub fn networks(&self) -> Future<Response> {
        self.send(Request::Networks)
    }

    /// Retrieve the channels the bot is in for a given network
    pub fn channels(&self, network: &str) -> Future<Response> {
        self.send(Request::Channels(String::from_str(network)))
    }

    /// Send a message to a specific channel using the PRIVMSG method
    pub fn message(&self, network: &str, channel: &str, message: &str) -> Future<Response> {
        self.send(Request::Message(
            String::from_str(network),
            String::from_str(channel),
            String::from_str(message)
        ))
    }

    /// Send a CTCP NOTICE to a specific channel
    pub fn notice(&self, network: &str, channel: &str, message: &str) -> Future<Response> {
        self.send(Request::Notice(
            String::from_str(network),
            String::from_str(channel),
            String::from_str(message)
        ))
    }

    /// Send a CTCP REQUEST to a specific channel
    pub fn ctcp(&self, network: &str, channel: &str, message: &str) -> Future<Response> {
        self.send(Request::Ctcp(
            String::from_str(network),
            String::from_str(channel),
            String::from_str(message)
        ))
    }

    /// Send a CTCP REPLY to a specific channel
    pub fn ctcp_reply(&self, network: &str, channel: &str, message: &str) -> Future<Response> {
        self.send(Request::CtcpReply(
            String::from_str(network),
            String::from_str(channel),
            String::from_str(message)
        ))
    }

    /// Send a CTCP ACTION to a specific channel
    pub fn action(&self, network: &str, channel: &str, message: &str) -> Future<Response> {
        self.send(Request::Action(
            String::from_str(network),
            String::from_str(channel),
            String::from_str(message)
        ))
    }

    // pub fn reply(&self, network: &str, channel: &str, message: &str) -> Future<Response> {
    //
    // }

    /// Send a request for the list of nicks in a channel
    /// Note that the response will not contain the names data, instead listen for a names event
    pub fn send_names(&self, network: &str, channel: &str) -> Future<Response> {
        self.send(Request::Names(String::from_str(network), String::from_str(channel)))
    }

    // pub fn names(&self, network: &str, channel: &str) -> Future<Event> {
    //
    // }

    /// Send a request for a whois of a specific nick on some network
    /// Note that the response will not contain the whois data, instead listen for a whois event
    pub fn send_whois(&self, network: &str, nick: &str) -> Future<Response> {
        self.send(Request::Whois(String::from_str(network), String::from_str(nick)))
    }

    // pub fn whois(&self, network: &str, nick: &str) -> Future<Event> {
    //
    // }

    /// Try to join a channel on some network
    pub fn join(&self, network: &str, channel: &str) -> Future<Response> {
        self.send(Request::Join(String::from_str(network), String::from_str(channel)))
    }

    /// Try to leave a channel on some network
    pub fn part(&self, network: &str, channel: &str) -> Future<Response> {
        self.send(Request::Part(String::from_str(network), String::from_str(channel)))
    }

    /// Retrieve the nickname of the bot on the given network
    pub fn nick(&self, network: &str) -> Future<Response> {
        self.send(Request::Nick(String::from_str(network)))
    }

    /// Send a handshake to the DaZeus server
    pub fn handshake(&self, name: &str, version: &str, config: Option<&str>) -> Future<Response> {
        let n = String::from_str(name);
        let v = String::from_str(version);
        let req = match config {
            Some(config_name) => Request::Handshake(n, v, Some(String::from_str(config_name))),
            None => Request::Handshake(n, v, None),
        };
        self.send(req)
    }

    /// Retrieve a config value from the DaZeus server config
    pub fn get_config(&self, name: &str, group: Option<&str>) -> Future<Response> {
        let n = String::from_str(name);
        let req = match group {
            Some(group_name) => Request::Config(n, Some(String::from_str(group_name))),
            None => Request::Config(n, None),
        };
        self.send(req)
    }

    /// Retrieve the character that is used by the bot for highlighting
    pub fn get_highlight_char(&self) -> Future<Response> {
        self.get_config("highlight", Some("core"))
    }

    /// Retrieve a property stored in the bot database
    pub fn get_property(&self, name: &str, scope: Scope) -> Future<Response> {
        self.send(Request::GetProperty(String::from_str(name), scope))
    }

    /// Set a property to be stored in the bot database
    pub fn set_property(&self, name: &str, value: &str, scope: Scope) -> Future<Response> {
        self.send(Request::SetProperty(String::from_str(name), String::from_str(value), scope))
    }

    /// Remove a property stored in the bot database
    pub fn unset_property(&self, name: &str, scope: Scope) -> Future<Response> {
        self.send(Request::UnsetProperty(String::from_str(name), scope))
    }

    /// Retrieve a list of keys starting with the common prefix with the given scope
    pub fn get_property_keys(&self, prefix: &str, scope: Scope) -> Future<Response> {
        self.send(Request::PropertyKeys(String::from_str(prefix), scope))
    }

    /// Set a permission to either allow or deny for a specific scope
    pub fn set_permission(&self, permission: &str, allow: bool, scope: Scope) -> Future<Response> {
        self.send(Request::SetPermission(String::from_str(permission), allow, scope))
    }

    /// Retrieve whether for some scope the given permission was set
    /// Will return the default if it was not
    pub fn has_permission(&self, permission: &str, default: bool, scope: Scope) -> Future<Response> {
        self.send(Request::HasPermission(String::from_str(permission), default, scope))
    }

    /// Remove a set permission from the bot
    pub fn unset_permission(&self, permission: &str, scope: Scope) -> Future<Response> {
        self.send(Request::UnsetPermission(String::from_str(permission), scope))
    }
}
<|endoftext|>


<commit_before>use collections::string::ToString;
use core::ops::*;

use bounded::Bounded;
use to_primitive::ToPrimitive;
use from_primitive::FromPrimitive;
use round::Round;
use one::One;
use sqrt::Sqrt;
use trig::Trig;
use zero::Zero;

pub trait Num:
    Clone + One + Zero
    + Bounded
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + PartialEq
    + PartialOrd
    + ToString

    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>

    + AddAssign<Self>
    + MulAssign<Self>
    + SubAssign<Self>
    + DivAssign<Self>
    + RemAssign<Self>
{
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!(50.min(&100), 50);
    /// assert_eq!(100.min(&50), 50);
    /// ~~~
    #[inline]
    fn min(&self, other: &Self) -> Self {
        if self < other {
            self.clone()
        } else {
            other.clone()
        }
    }
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!(50.max(&100), 100);
    /// assert_eq!(100.max(&50), 100);
    /// ~~~
    #[inline]
    fn max(&self, other: &Self) -> Self {
        if self > other {
            self.clone()
        } else {
            other.clone()
        }
    }
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!((-50).clamp(&0, &100), 0);
    /// assert_eq!(50.clamp(&0, &100), 50);
    /// assert_eq!(150.clamp(&0, &100), 100);
    /// ~~~
    #[inline]
    fn clamp(&self, min: &Self, max: &Self) -> Self {
        self.min(max).max(min)
    }
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!((-0.5).clamp01(), 0.0);
    /// assert_eq!(0.5.clamp01(), 0.5);
    /// assert_eq!(1.50.clamp01(), 1.0);
    /// ~~~
    #[inline(always)]
    fn clamp01(&self) -> Self {
        self.clamp(&Zero::zero(), &One::one())
    }
}

impl<T> Num for T where T:
    Copy + One + Zero
    + Bounded
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + PartialEq
    + PartialOrd
    + ToString

    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>

    + AddAssign<T>
    + MulAssign<T>
    + SubAssign<T>
    + DivAssign<T>
    + RemAssign<T> {}
<commit_msg>clean up<commit_after>use collections::string::ToString;
use core::ops::*;

use bounded::Bounded;
use to_primitive::ToPrimitive;
use from_primitive::FromPrimitive;
use round::Round;
use one::One;
use sqrt::Sqrt;
use trig::Trig;
use zero::Zero;

pub trait Num:
    Clone + One + Zero
    + Bounded
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + PartialEq
    + PartialOrd
    + ToString

    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>

    + AddAssign<Self>
    + MulAssign<Self>
    + SubAssign<Self>
    + DivAssign<Self>
    + RemAssign<Self>
{
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!(50.min(&100), 50);
    /// assert_eq!(100.min(&50), 50);
    /// ~~~
    #[inline]
    fn min(&self, other: &Self) -> Self {
        if self < other {
            self.clone()
        } else {
            other.clone()
        }
    }
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!(50.max(&100), 100);
    /// assert_eq!(100.max(&50), 100);
    /// ~~~
    #[inline]
    fn max(&self, other: &Self) -> Self {
        if self > other {
            self.clone()
        } else {
            other.clone()
        }
    }
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!((-50).clamp(&0, &100), 0);
    /// assert_eq!(50.clamp(&0, &100), 50);
    /// assert_eq!(150.clamp(&0, &100), 100);
    /// ~~~
    #[inline]
    fn clamp(&self, min: &Self, max: &Self) -> Self {
        self.min(max).max(min)
    }
    /// # Examples
    /// ~~~
    /// use num::Num;
    ///
    /// assert_eq!((-0.5).clamp01(), 0.0);
    /// assert_eq!(0.5.clamp01(), 0.5);
    /// assert_eq!(1.50.clamp01(), 1.0);
    /// ~~~
    #[inline(always)]
    fn clamp01(&self) -> Self {
        self.clamp(&Zero::zero(), &One::one())
    }
}

impl<T> Num for T where T:
    Clone + One + Zero
    + Bounded
    + ToPrimitive
    + FromPrimitive
    + Trig
    + Sqrt
    + Round
    + PartialEq
    + PartialOrd
    + ToString

    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>

    + AddAssign<T>
    + MulAssign<T>
    + SubAssign<T>
    + DivAssign<T>
    + RemAssign<T> {}
<|endoftext|>


<commit_before>#![feature(libc)]
extern crate libc;

use std::ffi::CString;

pub struct Lock {
  _fd: i32,
}

pub enum Error {
  InvalidFilename,
  Errno(i32),
}

#[repr(C)]
struct c_result {
  _fd:    i32,
  _errno: i32,
}

macro_rules! _create_lock_type {
  ($lock_type:ident, $c_lock_type:ident) => {
    extern {
      fn $c_lock_type(filename: *const libc::c_char) -> c_result;
    }

    pub fn $lock_type(filename: &str) -> Result<Lock, Error> {
      let raw_filename = CString::new(filename);

      if raw_filename.is_err() {
        return Err(Error::InvalidFilename);
      }

      unsafe {
        let my_result = $c_lock_type(raw_filename.unwrap().as_ptr());

        return match my_result._errno {
           0 => Ok(Lock{_fd: my_result._fd}),
          -1 => Err(Error::Errno(my_result._errno)),
           _ => unreachable!(),
        }
      }
    }
  };
}

_create_lock_type!(lock, c_lock);
_create_lock_type!(lock_wait, c_lock_wait);

extern {
  fn c_unlock(_fd: i32) -> c_result;
}

pub fn unlock(lock: &Lock) -> Result<bool, Error> {
  unsafe {
    let my_result = c_unlock(lock._fd);

    return match my_result._errno {
       0 => Ok(true),
      -1 => Err(Error::Errno(my_result._errno)),
       _ => unreachable!(),
    }
  }
}

#[allow(unused_must_use)]
impl Drop for Lock {
  fn drop(&mut self) {
    unlock(self);
  }
}
<commit_msg>Checking the wrong field<commit_after>#![feature(libc)]
extern crate libc;

use std::ffi::CString;

pub struct Lock {
  _fd: i32,
}

pub enum Error {
  InvalidFilename,
  Errno(i32),
}

#[repr(C)]
struct c_result {
  _fd:    i32,
  _errno: i32,
}

macro_rules! _create_lock_type {
  ($lock_type:ident, $c_lock_type:ident) => {
    extern {
      fn $c_lock_type(filename: *const libc::c_char) -> c_result;
    }

    pub fn $lock_type(filename: &str) -> Result<Lock, Error> {
      let raw_filename = CString::new(filename);

      if raw_filename.is_err() {
        return Err(Error::InvalidFilename);
      }

      unsafe {
        let my_result = $c_lock_type(raw_filename.unwrap().as_ptr());

        return match my_result._fd {
           0 => Ok(Lock{_fd: my_result._fd}),
          -1 => Err(Error::Errno(my_result._errno)),
           _ => unreachable!(),
        }
      }
    }
  };
}

_create_lock_type!(lock, c_lock);
_create_lock_type!(lock_wait, c_lock_wait);

extern {
  fn c_unlock(_fd: i32) -> c_result;
}

pub fn unlock(lock: &Lock) -> Result<bool, Error> {
  unsafe {
    let my_result = c_unlock(lock._fd);

    return match my_result._fd {
       0 => Ok(true),
      -1 => Err(Error::Errno(my_result._errno)),
       _ => unreachable!(),
    }
  }
}

#[allow(unused_must_use)]
impl Drop for Lock {
  fn drop(&mut self) {
    unlock(self);
  }
}
<|endoftext|>


<commit_before>//! GPIO wired to the LEDs of the Beaglebone

use just;

/// Root folder (modulo number) that contains the files that control the GPIO
static ROOT: &'static str = "/sys/class/leds/beaglebone:green:usr";

/// LED identifier
pub enum Number {
    /// First LED, by default is in heartbeat mode
    Zero = 0,
    /// Second LED, unused and powered off by default
    One = 1,
    /// Third LED, tracks CPU usage by default
    Three = 3,
    /// Fourth LED, tracks disk I/O by default
    Two = 2,
}

/// LED trigger modes
pub enum Trigger {
    Heartbeat,
    None,
    Timer,
}

impl Trigger {
    fn from_str(s: &str) -> Option<Trigger> {
        match s {
            "heartbeat" => Some(Trigger::Heartbeat),
            "none" => Some(Trigger::None),
            "timer" => Some(Trigger::Timer),
            _ => None,
        }
    }

    fn to_str(&self) -> &'static str {
        match *self {
            Trigger::Heartbeat => "heartbeat",
            Trigger::None => "none",
            Trigger::Timer => "timer",
        }
    }
}

/// An LED controller
pub struct Led {
    root: Path,
}

impl Led {
    /// Create access to an LED
    pub fn new(number: Number) -> Led {
        Led {
            root: Path::new(format!("{}{}", ROOT, number as u8)),
        }
    }

    /// Makes the LED blink
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Zero};
    ///
    /// // On for one second, off for half a second
    /// Led::new(Zero).blink(1000, 500);
    /// ```
    // XXX Not sure about how big can `on_ms` and `off_ms` be
    pub fn blink(&self, on_ms: u16, off_ms: u16) {
        self.set_trigger(Trigger::Timer);
        just::write(&self.root.join("delay_on"), format!("{}", on_ms)[]);
        just::write(&self.root.join("delay_off"), format!("{}", off_ms)[]);
    }

    /// Changes the brightness of the LED
    // XXX Is `u8` enough?
    pub fn set_brightness(&self, brightness: u8) {
        just::write(&self.root.join("brightness"), format!("{}", brightness)[])
    }

    /// Turns on the LED
    pub fn set_high(&self) {
        self.set_trigger(Trigger::None);
        self.set_brightness(1);
    }

    /// Turns off the LED
    pub fn set_low(&self) {
        self.set_trigger(Trigger::None);
        self.set_brightness(0);
    }

    /// Changes the trigger mode of the LED
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Heartbeat, Zero};
    ///
    /// Led::new(Zero).set_trigger(Heartbeat);
    /// println!("I'm alive!");
    /// ```
    pub fn set_trigger(&self, trigger: Trigger) {
        just::write(&self.root.join("trigger"), trigger.to_str());
    }

    /// Returns the current trigger mode the LED is using
    pub fn trigger(&self) -> Trigger {
        let s = just::read(&self.root.join("trigger"));
        match s[].split('[').skip(1).next().and_then(|s| s.split(']').next()) {
            Some(s) => match Trigger::from_str(s) {
                Some(trigger) => trigger,
                None => panic!("Unknown trigger mode: {}", s),
            },
            None => panic!("Failed to parse: {}", s),
        }
    }
}
<commit_msg>fix(warnings): add `#[deriving(Copy)]`<commit_after>//! GPIO wired to the LEDs of the Beaglebone

use just;

/// Root folder (modulo number) that contains the files that control the GPIO
static ROOT: &'static str = "/sys/class/leds/beaglebone:green:usr";

/// LED identifier
#[deriving(Copy)]
pub enum Number {
    /// First LED, by default is in heartbeat mode
    Zero = 0,
    /// Second LED, unused and powered off by default
    One = 1,
    /// Third LED, tracks CPU usage by default
    Three = 3,
    /// Fourth LED, tracks disk I/O by default
    Two = 2,
}

/// LED trigger modes
#[deriving(Copy)]
pub enum Trigger {
    Heartbeat,
    None,
    Timer,
}

impl Trigger {
    fn from_str(s: &str) -> Option<Trigger> {
        match s {
            "heartbeat" => Some(Trigger::Heartbeat),
            "none" => Some(Trigger::None),
            "timer" => Some(Trigger::Timer),
            _ => None,
        }
    }

    fn to_str(&self) -> &'static str {
        match *self {
            Trigger::Heartbeat => "heartbeat",
            Trigger::None => "none",
            Trigger::Timer => "timer",
        }
    }
}

/// An LED controller
pub struct Led {
    root: Path,
}

impl Led {
    /// Create access to an LED
    pub fn new(number: Number) -> Led {
        Led {
            root: Path::new(format!("{}{}", ROOT, number as u8)),
        }
    }

    /// Makes the LED blink
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Zero};
    ///
    /// // On for one second, off for half a second
    /// Led::new(Zero).blink(1000, 500);
    /// ```
    // XXX Not sure about how big can `on_ms` and `off_ms` be
    pub fn blink(&self, on_ms: u16, off_ms: u16) {
        self.set_trigger(Trigger::Timer);
        just::write(&self.root.join("delay_on"), format!("{}", on_ms)[]);
        just::write(&self.root.join("delay_off"), format!("{}", off_ms)[]);
    }

    /// Changes the brightness of the LED
    // XXX Is `u8` enough?
    pub fn set_brightness(&self, brightness: u8) {
        just::write(&self.root.join("brightness"), format!("{}", brightness)[])
    }

    /// Turns on the LED
    pub fn set_high(&self) {
        self.set_trigger(Trigger::None);
        self.set_brightness(1);
    }

    /// Turns off the LED
    pub fn set_low(&self) {
        self.set_trigger(Trigger::None);
        self.set_brightness(0);
    }

    /// Changes the trigger mode of the LED
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Heartbeat, Zero};
    ///
    /// Led::new(Zero).set_trigger(Heartbeat);
    /// println!("I'm alive!");
    /// ```
    pub fn set_trigger(&self, trigger: Trigger) {
        just::write(&self.root.join("trigger"), trigger.to_str());
    }

    /// Returns the current trigger mode the LED is using
    pub fn trigger(&self) -> Trigger {
        let s = just::read(&self.root.join("trigger"));
        match s[].split('[').skip(1).next().and_then(|s| s.split(']').next()) {
            Some(s) => match Trigger::from_str(s) {
                Some(trigger) => trigger,
                None => panic!("Unknown trigger mode: {}", s),
            },
            None => panic!("Failed to parse: {}", s),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(collections, core, libc)]
#![allow(raw_pointer_derive)]
#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

extern crate libc;

use std::ffi::CString;
use std::fmt;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    #[link(name = "intl")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    extern{}
}

mod ffi;

pub enum Object {
    Buffer(ffi::C_Buffer),
    Window(ffi::C_Window),
    Tabpage(ffi::C_Tabpage),
    Boolean(ffi::C_Boolean),
    Integer(ffi::C_Integer),
    Float(ffi::C_Float),
    String(String),
    Array(Array),
    Dictionary(ffi::C_Dictionary)
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Buffer(ref obj) => write!(f, "Buffer({:?})", obj),
            Object::Window(ref obj) => write!(f, "Window({:?})", obj),
            Object::Tabpage(ref obj) => write!(f, "Tabpage({:?})", obj),
            Object::Boolean(ref obj) => write!(f, "Boolean({:?})", obj),
            Object::Integer(ref obj) => write!(f, "Integer({:?})", obj),
            Object::Float(ref obj) => write!(f, "Float({:?})", obj),
            Object::String(ref obj) => write!(f, "String({:?})", obj),
            Object::Array(ref arr) => {
                write!(f, "Array(").ok();
                let obj = arr.unwrap_value();
                for i in range(0, obj.size) {
                    let inner_obj_opt = unsafe { c_object_to_object(obj.items.offset(i as isize)) };
                    if let Some(inner_obj) = inner_obj_opt {
                        write!(f, "{:?}", inner_obj).ok();
                        if i + 1 < obj.size {
                            write!(f, ", ").ok();
                        }
                    } else {
                        write!(f, "Nil ").ok();
                    }
                }
                write!(f, ")")
            },
            Object::Dictionary(ref obj) => write!(f, "Dictionary(Length: {:?})", obj.size)
        }
    }
}

unsafe fn c_object_to_object(obj: *mut ffi::C_Object) -> Option<Object> {
    match (*obj).object_type {
        ffi::ObjectType::BufferType =>
            Some(Object::Buffer((*(obj as *mut ffi::C_Object_Buffer)).data)),
        ffi::ObjectType::WindowType =>
            Some(Object::Window((*(obj as *mut ffi::C_Object_Window)).data)),
        ffi::ObjectType::TabpageType =>
            Some(Object::Tabpage((*(obj as *mut ffi::C_Object_Tabpage)).data)),
        ffi::ObjectType::NilType =>
            None,
        ffi::ObjectType::BooleanType =>
            Some(Object::Boolean((*(obj as *mut ffi::C_Object_Boolean)).data)),
        ffi::ObjectType::IntegerType =>
            Some(Object::Integer((*(obj as *mut ffi::C_Object_Integer)).data)),
        ffi::ObjectType::FloatType =>
            Some(Object::Float((*(obj as *mut ffi::C_Object_Float)).data)),
        ffi::ObjectType::StringType => {
            let vim_str: ffi::C_String = (*(obj as *mut ffi::C_Object_String)).data;
            let v = Vec::from_raw_buf(vim_str.data as *const u8, vim_str.size as usize);
            Some(Object::String(String::from_utf8_unchecked(v)))
        },
        ffi::ObjectType::ArrayType =>
            Some(Object::Array(Array::wrap_value((*(obj as *mut ffi::C_Object_Array)).data))),
        ffi::ObjectType::DictionaryType =>
            Some(Object::Dictionary((*(obj as *mut ffi::C_Object_Dictionary)).data))
    }
}

pub fn main_setup(args: &Vec<String>) -> i32 {
    let args_vec_cstr: Vec<CString> = args.iter().map(|s| CString::new(s.as_slice()).unwrap()).collect();
    let args_vec_ptr: Vec<*const ffi::c_char> = args_vec_cstr.iter().map(|s| s.as_ptr()).collect();
    unsafe { ffi::nvim_main_setup(args_vec_ptr.len() as i32, args_vec_ptr.as_ptr()) }
}

pub fn main_loop() -> i32 {
    unsafe { ffi::nvim_main_loop() }
}

pub fn channel_from_fds(read_fd: i32, write_fd: i32) -> u64 {
    unsafe { ffi::channel_from_fds(read_fd, write_fd) }
}

pub fn serialize_message(id: u64, method: &'static str, args: &Array) -> String {
    unsafe {
        let buf = ffi::vim_msgpack_new();
        let vim_str = ffi::C_String {data: method.as_ptr() as *const i8, size: method.len() as u64};
        ffi::vim_serialize_request(id, vim_str, args.unwrap_value(), buf);
        let v = Vec::from_raw_buf((*buf).data as *const u8, (*buf).size as usize);
        ffi::vim_msgpack_free(buf);
        String::from_utf8_unchecked(v)
    }
}

pub fn deserialize_message(message: &String) -> Array {
    let s = ffi::C_String {
        data: message.as_slice().as_ptr() as *const i8,
        size: message.len() as u64
    };
    let mut arr_raw = ffi::C_Array {
        items: ::std::ptr::null_mut(),
        size: 0,
        capacity: 0
    };
    unsafe { ffi::vim_msgpack_parse(s, &mut arr_raw) };
    Array::wrap_value(arr_raw)
}

pub struct Array {
    value: ffi::C_Array,
    is_owned: bool,
}

impl Array {
    pub fn new() -> Array {
        Array {
            value: ffi::C_Array {
                items: ::std::ptr::null_mut(),
                size: 0,
                capacity: 0
            },
            is_owned: true
        }
    }

    pub fn add_buffer(&mut self, val: ffi::C_Buffer) {
        unsafe { ffi::vim_array_add_buffer(val, &mut self.value) }
    }

    pub fn add_window(&mut self, val: ffi::C_Window) {
        unsafe { ffi::vim_array_add_window(val, &mut self.value) }
    }

    pub fn add_tabpage(&mut self, val: ffi::C_Tabpage) {
        unsafe { ffi::vim_array_add_tabpage(val, &mut self.value) }
    }

    pub fn add_nil(&mut self) {
        unsafe { ffi::vim_array_add_nil(&mut self.value) }
    }

    pub fn add_boolean(&mut self, val: ffi::C_Boolean) {
        unsafe { ffi::vim_array_add_boolean(val, &mut self.value) }
    }

    pub fn add_integer(&mut self, val: ffi::C_Integer) {
        unsafe { ffi::vim_array_add_integer(val, &mut self.value) }
    }

    pub fn add_float(&mut self, val: ffi::C_Float) {
        unsafe { ffi::vim_array_add_float(val, &mut self.value) }
    }

    pub fn add_string(&mut self, val: &str) {
        unsafe {
            // we need to copy the string into memory not managed by Rust,
            // so api_free_array can clear it
            let ptr = ffi::malloc(val.len() as u64);
            ::std::ptr::copy(ptr, val.as_ptr() as *const ffi::c_void, val.len());
            let vim_str = ffi::C_String {data: ptr as *const i8, size: val.len() as u64};
            ffi::vim_array_add_string(vim_str, &mut self.value)
        }
    }

    pub fn add_array(&mut self, val: &Array) {
        unsafe { ffi::vim_array_add_array(val.unwrap_value(), &mut self.value) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        unsafe { ffi::vim_array_add_dictionary(val, &mut self.value) }
    }

    pub fn get(&self, index: u64) -> Option<Object> {
        if index >= self.len() {
            return None;
        }
        unsafe { c_object_to_object(self.value.items.offset(index as isize)) }
    }

    pub fn len(&self) -> u64 {
        self.value.size
    }

    #[doc(hidden)]
    pub fn unwrap_value(&self) -> ffi::C_Array {
        self.value
    }

    #[doc(hidden)]
    pub fn wrap_value(c_array: ffi::C_Array) -> Array {
        Array {
            value: c_array,
            is_owned: false
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        if self.is_owned {
            unsafe { ffi::api_free_array(self.value) };
        }
    }
}

impl fmt::Debug for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Array(").ok();
        for i in range(0, self.len()) {
            if let Some(obj) = self.get(i) {
                write!(f, "{:?}", obj).ok();
            } else {
                write!(f, "Nil").ok();
            }
            if i + 1 < self.len() {
                write!(f, ", ").ok();
            }
        }
        write!(f, ")")
    }
}

#[test]
fn test_request() {
    let mut args = Array::new();
    args.add_integer(80);
    args.add_integer(24);
    args.add_string("hello");

    let msg = serialize_message(1, "test", &args);
    let arr = deserialize_message(&msg);
    println!("LENGTH: {}", arr.len());
    for i in range(0, arr.len()) {
        println!("{:?}", arr.get(i));
    }
}
<commit_msg>Use new range syntax<commit_after>#![feature(collections, core, libc)]
#![allow(raw_pointer_derive)]
#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

extern crate libc;

use std::ffi::CString;
use std::fmt;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    #[link(name = "intl")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    extern{}
}

mod ffi;

pub enum Object {
    Buffer(ffi::C_Buffer),
    Window(ffi::C_Window),
    Tabpage(ffi::C_Tabpage),
    Boolean(ffi::C_Boolean),
    Integer(ffi::C_Integer),
    Float(ffi::C_Float),
    String(String),
    Array(Array),
    Dictionary(ffi::C_Dictionary)
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Buffer(ref obj) => write!(f, "Buffer({:?})", obj),
            Object::Window(ref obj) => write!(f, "Window({:?})", obj),
            Object::Tabpage(ref obj) => write!(f, "Tabpage({:?})", obj),
            Object::Boolean(ref obj) => write!(f, "Boolean({:?})", obj),
            Object::Integer(ref obj) => write!(f, "Integer({:?})", obj),
            Object::Float(ref obj) => write!(f, "Float({:?})", obj),
            Object::String(ref obj) => write!(f, "String({:?})", obj),
            Object::Array(ref arr) => {
                write!(f, "Array(").ok();
                let obj = arr.unwrap_value();
                for i in 0..obj.size {
                    let inner_obj_opt = unsafe { c_object_to_object(obj.items.offset(i as isize)) };
                    if let Some(inner_obj) = inner_obj_opt {
                        write!(f, "{:?}", inner_obj).ok();
                        if i + 1 < obj.size {
                            write!(f, ", ").ok();
                        }
                    } else {
                        write!(f, "Nil ").ok();
                    }
                }
                write!(f, ")")
            },
            Object::Dictionary(ref obj) => write!(f, "Dictionary(Length: {:?})", obj.size)
        }
    }
}

unsafe fn c_object_to_object(obj: *mut ffi::C_Object) -> Option<Object> {
    match (*obj).object_type {
        ffi::ObjectType::BufferType =>
            Some(Object::Buffer((*(obj as *mut ffi::C_Object_Buffer)).data)),
        ffi::ObjectType::WindowType =>
            Some(Object::Window((*(obj as *mut ffi::C_Object_Window)).data)),
        ffi::ObjectType::TabpageType =>
            Some(Object::Tabpage((*(obj as *mut ffi::C_Object_Tabpage)).data)),
        ffi::ObjectType::NilType =>
            None,
        ffi::ObjectType::BooleanType =>
            Some(Object::Boolean((*(obj as *mut ffi::C_Object_Boolean)).data)),
        ffi::ObjectType::IntegerType =>
            Some(Object::Integer((*(obj as *mut ffi::C_Object_Integer)).data)),
        ffi::ObjectType::FloatType =>
            Some(Object::Float((*(obj as *mut ffi::C_Object_Float)).data)),
        ffi::ObjectType::StringType => {
            let vim_str: ffi::C_String = (*(obj as *mut ffi::C_Object_String)).data;
            let v = Vec::from_raw_buf(vim_str.data as *const u8, vim_str.size as usize);
            Some(Object::String(String::from_utf8_unchecked(v)))
        },
        ffi::ObjectType::ArrayType =>
            Some(Object::Array(Array::wrap_value((*(obj as *mut ffi::C_Object_Array)).data))),
        ffi::ObjectType::DictionaryType =>
            Some(Object::Dictionary((*(obj as *mut ffi::C_Object_Dictionary)).data))
    }
}

pub fn main_setup(args: &Vec<String>) -> i32 {
    let args_vec_cstr: Vec<CString> = args.iter().map(|s| CString::new(s.as_slice()).unwrap()).collect();
    let args_vec_ptr: Vec<*const ffi::c_char> = args_vec_cstr.iter().map(|s| s.as_ptr()).collect();
    unsafe { ffi::nvim_main_setup(args_vec_ptr.len() as i32, args_vec_ptr.as_ptr()) }
}

pub fn main_loop() -> i32 {
    unsafe { ffi::nvim_main_loop() }
}

pub fn channel_from_fds(read_fd: i32, write_fd: i32) -> u64 {
    unsafe { ffi::channel_from_fds(read_fd, write_fd) }
}

pub fn serialize_message(id: u64, method: &'static str, args: &Array) -> String {
    unsafe {
        let buf = ffi::vim_msgpack_new();
        let vim_str = ffi::C_String {data: method.as_ptr() as *const i8, size: method.len() as u64};
        ffi::vim_serialize_request(id, vim_str, args.unwrap_value(), buf);
        let v = Vec::from_raw_buf((*buf).data as *const u8, (*buf).size as usize);
        ffi::vim_msgpack_free(buf);
        String::from_utf8_unchecked(v)
    }
}

pub fn deserialize_message(message: &String) -> Array {
    let s = ffi::C_String {
        data: message.as_slice().as_ptr() as *const i8,
        size: message.len() as u64
    };
    let mut arr_raw = ffi::C_Array {
        items: ::std::ptr::null_mut(),
        size: 0,
        capacity: 0
    };
    unsafe { ffi::vim_msgpack_parse(s, &mut arr_raw) };
    Array::wrap_value(arr_raw)
}

pub struct Array {
    value: ffi::C_Array,
    is_owned: bool,
}

impl Array {
    pub fn new() -> Array {
        Array {
            value: ffi::C_Array {
                items: ::std::ptr::null_mut(),
                size: 0,
                capacity: 0
            },
            is_owned: true
        }
    }

    pub fn add_buffer(&mut self, val: ffi::C_Buffer) {
        unsafe { ffi::vim_array_add_buffer(val, &mut self.value) }
    }

    pub fn add_window(&mut self, val: ffi::C_Window) {
        unsafe { ffi::vim_array_add_window(val, &mut self.value) }
    }

    pub fn add_tabpage(&mut self, val: ffi::C_Tabpage) {
        unsafe { ffi::vim_array_add_tabpage(val, &mut self.value) }
    }

    pub fn add_nil(&mut self) {
        unsafe { ffi::vim_array_add_nil(&mut self.value) }
    }

    pub fn add_boolean(&mut self, val: ffi::C_Boolean) {
        unsafe { ffi::vim_array_add_boolean(val, &mut self.value) }
    }

    pub fn add_integer(&mut self, val: ffi::C_Integer) {
        unsafe { ffi::vim_array_add_integer(val, &mut self.value) }
    }

    pub fn add_float(&mut self, val: ffi::C_Float) {
        unsafe { ffi::vim_array_add_float(val, &mut self.value) }
    }

    pub fn add_string(&mut self, val: &str) {
        unsafe {
            // we need to copy the string into memory not managed by Rust,
            // so api_free_array can clear it
            let ptr = ffi::malloc(val.len() as u64);
            ::std::ptr::copy(ptr, val.as_ptr() as *const ffi::c_void, val.len());
            let vim_str = ffi::C_String {data: ptr as *const i8, size: val.len() as u64};
            ffi::vim_array_add_string(vim_str, &mut self.value)
        }
    }

    pub fn add_array(&mut self, val: &Array) {
        unsafe { ffi::vim_array_add_array(val.unwrap_value(), &mut self.value) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        unsafe { ffi::vim_array_add_dictionary(val, &mut self.value) }
    }

    pub fn get(&self, index: u64) -> Option<Object> {
        if index >= self.len() {
            return None;
        }
        unsafe { c_object_to_object(self.value.items.offset(index as isize)) }
    }

    pub fn len(&self) -> u64 {
        self.value.size
    }

    #[doc(hidden)]
    pub fn unwrap_value(&self) -> ffi::C_Array {
        self.value
    }

    #[doc(hidden)]
    pub fn wrap_value(c_array: ffi::C_Array) -> Array {
        Array {
            value: c_array,
            is_owned: false
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        if self.is_owned {
            unsafe { ffi::api_free_array(self.value) };
        }
    }
}

impl fmt::Debug for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Array(").ok();
        for i in 0..self.len() {
            if let Some(obj) = self.get(i) {
                write!(f, "{:?}", obj).ok();
            } else {
                write!(f, "Nil").ok();
            }
            if i + 1 < self.len() {
                write!(f, ", ").ok();
            }
        }
        write!(f, ")")
    }
}

#[test]
fn test_request() {
    let mut args = Array::new();
    args.add_integer(80);
    args.add_integer(24);
    args.add_string("hello");

    let msg = serialize_message(1, "test", &args);
    let arr = deserialize_message(&msg);
    println!("LENGTH: {}", arr.len());
    for i in 0..arr.len() {
        println!("{:?}", arr.get(i));
    }
}
<|endoftext|>