<commit_before>use arch::CapstoneBuilder;
use capstone_sys::cs_opt_value::*;
use capstone_sys::*;
use constants::{Arch, Endian, ExtraMode, Mode, OptValue, Syntax};
use error::*;
use instruction::{Insn, InsnDetail, InsnGroupId, InsnId, Instructions, RegId};
use std::convert::From;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::{c_int, c_uint, c_void};

/// An instance of the capstone disassembler
#[derive(Debug)]
pub struct Capstone<'cs> {
    /// Opaque handle to cs_engine
    /// Stored as a pointer to ensure `Capstone` is `!Send`/`!Sync`
    csh: *mut c_void,

    /// Internal mode bitfield
    mode: cs_mode,

    /// Internal endian bitfield
    endian: cs_mode,

    /// Syntax
    syntax: cs_opt_value::Type,

    /// Internal extra mode bitfield
    extra_mode: cs_mode,

    /// Whether to get extra details when disassembling
    detail_enabled: bool,

    /// We *must* set `mode`, `extra_mode`, and `endian` at once because `capstone`
    /// handles them inside the arch-specific handler. We store the bitwise OR of these flags that
    /// can be passed directly to `cs_option()`.
    raw_mode: cs_mode,

    /// Architecture
    arch: Arch,

    _marker: PhantomData<&'cs mut c_void>,
}

/// Defines a setter on `Capstone` that speculatively changes the arch-specific mode (which
/// includes `mode`, `endian`, and `extra_mode`). The setter takes a `capstone-rs` type and changes
/// the internal `capstone-sys` type.
macro_rules! define_set_mode {
    (
        $( #[$func_attr:meta] )*
        => $($visibility:ident)*, $fn_name:ident,
            $opt_type:ident, $param_name:ident : $param_type:ident ;
        $cs_base_type:ident
    ) => {
        $( #[$func_attr] )*
        $($visibility)* fn $fn_name(&mut self, $param_name: $param_type) -> CsResult<()> {
            let old_val = self.$param_name;
            self.$param_name = $cs_base_type::from($param_name);

            let old_raw_mode = self.raw_mode;
            let new_raw_mode = self.update_raw_mode();

            let result = self._set_cs_option(
                cs_opt_type::$opt_type,
                new_raw_mode.0 as usize,
            );

            if result.is_err() {
                // On error, restore old values
                self.raw_mode = old_raw_mode;
                self.$param_name = old_val;
            }

            result
        }
    }
}

/// Represents that no extra modes are enabled. Can be passed to `Capstone::new_raw()` as the
/// `extra_mode` argument.
pub static NO_EXTRA_MODE: EmptyExtraModeIter = EmptyExtraModeIter(PhantomData);

/// Represents an empty set of `ExtraMode`.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct EmptyExtraModeIter(PhantomData<()>);

impl Iterator for EmptyExtraModeIter {
    type Item = ExtraMode;

    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}

impl<'cs> Capstone<'cs> {
    /// Create a new instance of the decompiler using the builder pattern interface.
    /// This is the recommended interface to `Capstone`.
    ///
    /// ```
    /// use capstone::prelude::*;
    /// let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build();
    /// ```
    pub fn new() -> CapstoneBuilder {
        CapstoneBuilder::new()
    }

    /// Create a new instance of the decompiler using the "raw" interface.
    /// The user must ensure that only sensible `Arch`/`Mode` combinations are used.
    ///
    /// ```
    /// use capstone::{Arch, Capstone, NO_EXTRA_MODE, Mode};
    /// let cs = Capstone::new_raw(Arch::X86, Mode::Mode64, NO_EXTRA_MODE, None);
    /// assert!(cs.is_ok());
    /// ```
    pub fn new_raw<T: Iterator<Item = ExtraMode>>(
        arch: Arch,
        mode: Mode,
        extra_mode: T,
        endian: Option<Endian>,
    ) -> CsResult<Capstone<'cs>> {
        let mut handle: csh = 0;
        let csarch: cs_arch = arch.into();
        let csmode: cs_mode = mode.into();

        let endian = match endian {
            Some(endian) => cs_mode::from(endian),
            None => cs_mode(0),
        };
        let extra_mode = Self::extra_mode_value(extra_mode);

        let combined_mode = csmode | endian | extra_mode;
        let err = unsafe { cs_open(csarch, combined_mode, &mut handle) };

        if cs_err::CS_ERR_OK == err {
            let syntax = CS_OPT_SYNTAX_DEFAULT;
            let raw_mode = cs_mode(0);
            let detail_enabled = false;

            let mut cs = Capstone {
                csh: handle as *mut c_void,
                syntax,
                endian,
                mode: csmode,
                extra_mode,
                detail_enabled,
                raw_mode,
                arch,
                _marker: PhantomData,
            };
            cs.update_raw_mode();
            Ok(cs)
        } else {
            Err(err.into())
        }
    }

    /// Disassemble all instructions in buffer
    pub fn disasm_all<'s, 'c, 'a>(
        &'s mut self,
        code: &'c [u8],
        addr: u64,
    ) -> CsResult<Instructions<'a>> {
        self.disasm(code, addr, 0)
    }

    /// Disassemble `count` instructions in `code`
    pub fn disasm_count<'s, 'c, 'a>(
        &'s mut self,
        code: &'c [u8],
        addr: u64,
        count: usize,
    ) -> CsResult<Instructions<'a>> {
        if count == 0 {
            return Err(Error::CustomError("Invalid dissasemble count; must be > 0"));
        }
        self.disasm(code, addr, count)
    }

    /// Disassembles a `&[u8]` full of instructions.
    ///
    /// Pass `count = 0` to disassemble all instructions in the buffer.
    fn disasm<'a>(&mut self, code: &[u8], addr: u64, count: usize) -> CsResult<Instructions<'a>> {
        let mut ptr: *mut cs_insn = unsafe { mem::zeroed() };
        let insn_count = unsafe {
            cs_disasm(
                self.csh(),
                code.as_ptr(),
                code.len() as usize,
                addr,
                count as usize,
                &mut ptr,
            )
        };
        if insn_count == 0 {
            match self.error_result() {
                Ok(_) => Ok(Instructions::new_empty()),
                Err(err) => Err(err),
            }
        } else {
            Ok(unsafe { Instructions::from_raw_parts(ptr, insn_count) })
        }
    }

    /// Returns csh handle
    #[inline]
    fn csh(&self) -> csh {
        self.csh as csh
    }

    /// Returns the raw mode value, which is useful for debugging
    #[allow(dead_code)]
    pub(crate) fn raw_mode(&self) -> cs_mode {
        self.raw_mode
    }

    /// Update `raw_mode` with the bitwise OR of `mode`, `extra_mode`, and `endian`.
    ///
    /// Returns the new `raw_mode`.
    fn update_raw_mode(&mut self) -> cs_mode {
        self.raw_mode = self.mode | self.extra_mode | self.endian;
        self.raw_mode
    }

    /// Return the integer value used by capstone to represent the set of extra modes
    fn extra_mode_value<T: Iterator<Item = ExtraMode>>(extra_mode: T) -> cs_mode {
        // Bitwise OR extra modes
        extra_mode.fold(cs_mode(0), |acc, x| acc | cs_mode::from(x))
    }

    /// Set extra modes in addition to normal `mode`
    pub fn set_extra_mode<T: Iterator<Item = ExtraMode>>(&mut self, extra_mode: T) -> CsResult<()> {
        let old_val = self.extra_mode;

        self.extra_mode = Self::extra_mode_value(extra_mode);

        let old_mode = self.raw_mode;
        let new_mode = self.update_raw_mode();
        let result = self._set_cs_option(cs_opt_type::CS_OPT_MODE, new_mode.0 as usize);

        if result.is_err() {
            // On error, restore old values
            self.raw_mode = old_mode;
            self.extra_mode = old_val;
        }

        result
    }

    /// Set the assembly syntax (has no effect on some platforms)
    pub fn set_syntax(&mut self, syntax: Syntax) -> CsResult<()> {
        // Todo(tmfink) check for valid syntax
        let syntax_int = cs_opt_value::Type::from(syntax);
        let result = self._set_cs_option(cs_opt_type::CS_OPT_SYNTAX, syntax_int as usize);

        if result.is_ok() {
            self.syntax = syntax_int;
        }

        result
    }

    define_set_mode!(
    /// Set the endianness (has no effect on some platforms).
    => pub, set_endian, CS_OPT_MODE, endian : Endian; cs_mode);
    define_set_mode!(
    /// Sets the engine's disassembly mode.
    /// Be careful, various combinations of modes aren't supported
    /// See the capstone-sys documentation for more information.
    => pub, set_mode, CS_OPT_MODE, mode : Mode; cs_mode);

    /// Returns a `CsResult` based on current `errno`.
    /// If the `errno` is `CS_ERR_OK`, then `Ok(())` is returned. Otherwise, the error is returned.
    fn error_result(&self) -> CsResult<()> {
        let errno = unsafe { cs_errno(self.csh()) };
        if errno == cs_err::CS_ERR_OK {
            Ok(())
        } else {
            Err(errno.into())
        }
    }

    /// Sets disassembling options at runtime.
    ///
    /// Acts as a safe wrapper around capstone's `cs_option`.
    fn _set_cs_option(&mut self, option_type: cs_opt_type, option_value: usize) -> CsResult<()> {
        let err = unsafe { cs_option(self.csh(), option_type, option_value) };

        if cs_err::CS_ERR_OK == err {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    /// Controls whether to capstone will generate extra details about disassembled instructions.
    ///
    /// Pass `true` to enable detail or `false` to disable detail.
    pub fn set_detail(&mut self, enable_detail: bool) -> CsResult<()> {
        let option_value: usize = OptValue::from(enable_detail).0 as usize;
        let result = self._set_cs_option(cs_opt_type::CS_OPT_DETAIL, option_value);

        // Only update internal state on success
        if result.is_ok() {
            self.detail_enabled = enable_detail;
        }

        result
    }

    /// Converts a register id `reg_id` to a `String` containing the register name.
    pub fn reg_name(&self, reg_id: RegId) -> Option<String> {
        let reg_name = unsafe {
            let _reg_name = cs_reg_name(self.csh(), c_uint::from(reg_id.0));
            if _reg_name.is_null() {
                return None;
            }

            CStr::from_ptr(_reg_name).to_string_lossy().into_owned()
        };

        Some(reg_name)
    }

    /// Converts an instruction id `insn_id` to a `String` containing the instruction name.
    ///
    /// Note: This function ignores the current syntax and uses the default syntax.
    pub fn insn_name(&self, insn_id: InsnId) -> Option<String> {
        let insn_name = unsafe {
            let _insn_name = cs_insn_name(self.csh(), insn_id.0 as c_uint);
            if _insn_name.is_null() {
                return None;
            }
            CStr::from_ptr(_insn_name).to_string_lossy().into_owned()
        };

        Some(insn_name)
    }

    /// Converts a group id `group_id` to a `String` containing the group name.
    pub fn group_name(&self, group_id: InsnGroupId) -> Option<String> {
        let group_name = unsafe {
            let _group_name = cs_group_name(self.csh(), c_uint::from(group_id.0));
            if _group_name.is_null() {
                return None;
            }

            CStr::from_ptr(_group_name).to_string_lossy().into_owned()
        };

        Some(group_name)
    }

    /// Returns `Detail` structure for a given instruction
    ///
    /// Requires:
    ///
    /// 1. Instruction was created with detail enabled
    /// 2. Skipdata is disabled
    /// 3. Capstone was not compiled in diet mode
    pub fn insn_detail<'s, 'i: 's>(&'s self, insn: &'i Insn) -> CsResult<InsnDetail<'i>> {
        if !self.detail_enabled {
            Err(Error::DetailOff)
        } else if insn.id().0 == 0 {
            Err(Error::IrrelevantDataInSkipData)
        } else if Self::is_diet() {
            Err(Error::IrrelevantDataInDiet)
        } else {
            Ok(unsafe { insn.detail(self.arch) })
        }
    }

    /// Returns a tuple (major, minor) indicating the version of the capstone C library.
    pub fn lib_version() -> (u32, u32) {
        let mut major: c_int = 0;
        let mut minor: c_int = 0;
        let major_ptr: *mut c_int = &mut major;
        let minor_ptr: *mut c_int = &mut minor;

        // We can ignore the "hexical" version returned by capstone because we already have the
        // major and minor versions
        let _ = unsafe { cs_version(major_ptr, minor_ptr) };

        (major as u32, minor as u32)
    }

    /// Returns whether the capstone library supports a given architecture.
    pub fn supports_arch(arch: Arch) -> bool {
        unsafe { cs_support(arch as c_int) }
    }

    /// Returns whether the capstone library was compiled in diet mode.
    pub fn is_diet() -> bool {
        unsafe { cs_support(CS_SUPPORT_DIET as c_int) }
    }
}

impl<'cs> Drop for Capstone<'cs> {
    fn drop(&mut self) {
        unsafe { cs_close(&mut self.csh()) };
    }
}
<commit_msg>Ellide lifetimes for disasm() methods<commit_after>use arch::CapstoneBuilder;
use capstone_sys::cs_opt_value::*;
use capstone_sys::*;
use constants::{Arch, Endian, ExtraMode, Mode, OptValue, Syntax};
use error::*;
use instruction::{Insn, InsnDetail, InsnGroupId, InsnId, Instructions, RegId};
use std::convert::From;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem;
use std::os::raw::{c_int, c_uint, c_void};

/// An instance of the capstone disassembler
#[derive(Debug)]
pub struct Capstone<'cs> {
    /// Opaque handle to cs_engine
    /// Stored as a pointer to ensure `Capstone` is `!Send`/`!Sync`
    csh: *mut c_void,

    /// Internal mode bitfield
    mode: cs_mode,

    /// Internal endian bitfield
    endian: cs_mode,

    /// Syntax
    syntax: cs_opt_value::Type,

    /// Internal extra mode bitfield
    extra_mode: cs_mode,

    /// Whether to get extra details when disassembling
    detail_enabled: bool,

    /// We *must* set `mode`, `extra_mode`, and `endian` at once because `capstone`
    /// handles them inside the arch-specific handler. We store the bitwise OR of these flags that
    /// can be passed directly to `cs_option()`.
    raw_mode: cs_mode,

    /// Architecture
    arch: Arch,

    _marker: PhantomData<&'cs mut c_void>,
}

/// Defines a setter on `Capstone` that speculatively changes the arch-specific mode (which
/// includes `mode`, `endian`, and `extra_mode`). The setter takes a `capstone-rs` type and changes
/// the internal `capstone-sys` type.
macro_rules! define_set_mode {
    (
        $( #[$func_attr:meta] )*
        => $($visibility:ident)*, $fn_name:ident,
            $opt_type:ident, $param_name:ident : $param_type:ident ;
        $cs_base_type:ident
    ) => {
        $( #[$func_attr] )*
        $($visibility)* fn $fn_name(&mut self, $param_name: $param_type) -> CsResult<()> {
            let old_val = self.$param_name;
            self.$param_name = $cs_base_type::from($param_name);

            let old_raw_mode = self.raw_mode;
            let new_raw_mode = self.update_raw_mode();

            let result = self._set_cs_option(
                cs_opt_type::$opt_type,
                new_raw_mode.0 as usize,
            );

            if result.is_err() {
                // On error, restore old values
                self.raw_mode = old_raw_mode;
                self.$param_name = old_val;
            }

            result
        }
    }
}

/// Represents that no extra modes are enabled. Can be passed to `Capstone::new_raw()` as the
/// `extra_mode` argument.
pub static NO_EXTRA_MODE: EmptyExtraModeIter = EmptyExtraModeIter(PhantomData);

/// Represents an empty set of `ExtraMode`.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub struct EmptyExtraModeIter(PhantomData<()>);

impl Iterator for EmptyExtraModeIter {
    type Item = ExtraMode;

    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}

impl<'cs> Capstone<'cs> {
    /// Create a new instance of the decompiler using the builder pattern interface.
    /// This is the recommended interface to `Capstone`.
    ///
    /// ```
    /// use capstone::prelude::*;
    /// let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build();
    /// ```
    pub fn new() -> CapstoneBuilder {
        CapstoneBuilder::new()
    }

    /// Create a new instance of the decompiler using the "raw" interface.
    /// The user must ensure that only sensible `Arch`/`Mode` combinations are used.
    ///
    /// ```
    /// use capstone::{Arch, Capstone, NO_EXTRA_MODE, Mode};
    /// let cs = Capstone::new_raw(Arch::X86, Mode::Mode64, NO_EXTRA_MODE, None);
    /// assert!(cs.is_ok());
    /// ```
    pub fn new_raw<T: Iterator<Item = ExtraMode>>(
        arch: Arch,
        mode: Mode,
        extra_mode: T,
        endian: Option<Endian>,
    ) -> CsResult<Capstone<'cs>> {
        let mut handle: csh = 0;
        let csarch: cs_arch = arch.into();
        let csmode: cs_mode = mode.into();

        let endian = match endian {
            Some(endian) => cs_mode::from(endian),
            None => cs_mode(0),
        };
        let extra_mode = Self::extra_mode_value(extra_mode);

        let combined_mode = csmode | endian | extra_mode;
        let err = unsafe { cs_open(csarch, combined_mode, &mut handle) };

        if cs_err::CS_ERR_OK == err {
            let syntax = CS_OPT_SYNTAX_DEFAULT;
            let raw_mode = cs_mode(0);
            let detail_enabled = false;

            let mut cs = Capstone {
                csh: handle as *mut c_void,
                syntax,
                endian,
                mode: csmode,
                extra_mode,
                detail_enabled,
                raw_mode,
                arch,
                _marker: PhantomData,
            };
            cs.update_raw_mode();
            Ok(cs)
        } else {
            Err(err.into())
        }
    }

    /// Disassemble all instructions in buffer
    pub fn disasm_all<'a>(
        &mut self,
        code: &[u8],
        addr: u64,
    ) -> CsResult<Instructions<'a>> {
        self.disasm(code, addr, 0)
    }

    /// Disassemble `count` instructions in `code`
    pub fn disasm_count<'a>(
        &mut self,
        code: &[u8],
        addr: u64,
        count: usize,
    ) -> CsResult<Instructions<'a>> {
        if count == 0 {
            return Err(Error::CustomError("Invalid dissasemble count; must be > 0"));
        }
        self.disasm(code, addr, count)
    }

    /// Disassembles a `&[u8]` full of instructions.
    ///
    /// Pass `count = 0` to disassemble all instructions in the buffer.
    fn disasm<'a>(&mut self, code: &[u8], addr: u64, count: usize) -> CsResult<Instructions<'a>> {
        let mut ptr: *mut cs_insn = unsafe { mem::zeroed() };
        let insn_count = unsafe {
            cs_disasm(
                self.csh(),
                code.as_ptr(),
                code.len() as usize,
                addr,
                count as usize,
                &mut ptr,
            )
        };
        if insn_count == 0 {
            match self.error_result() {
                Ok(_) => Ok(Instructions::new_empty()),
                Err(err) => Err(err),
            }
        } else {
            Ok(unsafe { Instructions::from_raw_parts(ptr, insn_count) })
        }
    }

    /// Returns csh handle
    #[inline]
    fn csh(&self) -> csh {
        self.csh as csh
    }

    /// Returns the raw mode value, which is useful for debugging
    #[allow(dead_code)]
    pub(crate) fn raw_mode(&self) -> cs_mode {
        self.raw_mode
    }

    /// Update `raw_mode` with the bitwise OR of `mode`, `extra_mode`, and `endian`.
    ///
    /// Returns the new `raw_mode`.
    fn update_raw_mode(&mut self) -> cs_mode {
        self.raw_mode = self.mode | self.extra_mode | self.endian;
        self.raw_mode
    }

    /// Return the integer value used by capstone to represent the set of extra modes
    fn extra_mode_value<T: Iterator<Item = ExtraMode>>(extra_mode: T) -> cs_mode {
        // Bitwise OR extra modes
        extra_mode.fold(cs_mode(0), |acc, x| acc | cs_mode::from(x))
    }

    /// Set extra modes in addition to normal `mode`
    pub fn set_extra_mode<T: Iterator<Item = ExtraMode>>(&mut self, extra_mode: T) -> CsResult<()> {
        let old_val = self.extra_mode;

        self.extra_mode = Self::extra_mode_value(extra_mode);

        let old_mode = self.raw_mode;
        let new_mode = self.update_raw_mode();
        let result = self._set_cs_option(cs_opt_type::CS_OPT_MODE, new_mode.0 as usize);

        if result.is_err() {
            // On error, restore old values
            self.raw_mode = old_mode;
            self.extra_mode = old_val;
        }

        result
    }

    /// Set the assembly syntax (has no effect on some platforms)
    pub fn set_syntax(&mut self, syntax: Syntax) -> CsResult<()> {
        // Todo(tmfink) check for valid syntax
        let syntax_int = cs_opt_value::Type::from(syntax);
        let result = self._set_cs_option(cs_opt_type::CS_OPT_SYNTAX, syntax_int as usize);

        if result.is_ok() {
            self.syntax = syntax_int;
        }

        result
    }

    define_set_mode!(
    /// Set the endianness (has no effect on some platforms).
    => pub, set_endian, CS_OPT_MODE, endian : Endian; cs_mode);
    define_set_mode!(
    /// Sets the engine's disassembly mode.
    /// Be careful, various combinations of modes aren't supported
    /// See the capstone-sys documentation for more information.
    => pub, set_mode, CS_OPT_MODE, mode : Mode; cs_mode);

    /// Returns a `CsResult` based on current `errno`.
    /// If the `errno` is `CS_ERR_OK`, then `Ok(())` is returned. Otherwise, the error is returned.
    fn error_result(&self) -> CsResult<()> {
        let errno = unsafe { cs_errno(self.csh()) };
        if errno == cs_err::CS_ERR_OK {
            Ok(())
        } else {
            Err(errno.into())
        }
    }

    /// Sets disassembling options at runtime.
    ///
    /// Acts as a safe wrapper around capstone's `cs_option`.
    fn _set_cs_option(&mut self, option_type: cs_opt_type, option_value: usize) -> CsResult<()> {
        let err = unsafe { cs_option(self.csh(), option_type, option_value) };

        if cs_err::CS_ERR_OK == err {
            Ok(())
        } else {
            Err(err.into())
        }
    }

    /// Controls whether to capstone will generate extra details about disassembled instructions.
    ///
    /// Pass `true` to enable detail or `false` to disable detail.
    pub fn set_detail(&mut self, enable_detail: bool) -> CsResult<()> {
        let option_value: usize = OptValue::from(enable_detail).0 as usize;
        let result = self._set_cs_option(cs_opt_type::CS_OPT_DETAIL, option_value);

        // Only update internal state on success
        if result.is_ok() {
            self.detail_enabled = enable_detail;
        }

        result
    }

    /// Converts a register id `reg_id` to a `String` containing the register name.
    pub fn reg_name(&self, reg_id: RegId) -> Option<String> {
        let reg_name = unsafe {
            let _reg_name = cs_reg_name(self.csh(), c_uint::from(reg_id.0));
            if _reg_name.is_null() {
                return None;
            }

            CStr::from_ptr(_reg_name).to_string_lossy().into_owned()
        };

        Some(reg_name)
    }

    /// Converts an instruction id `insn_id` to a `String` containing the instruction name.
    ///
    /// Note: This function ignores the current syntax and uses the default syntax.
    pub fn insn_name(&self, insn_id: InsnId) -> Option<String> {
        let insn_name = unsafe {
            let _insn_name = cs_insn_name(self.csh(), insn_id.0 as c_uint);
            if _insn_name.is_null() {
                return None;
            }
            CStr::from_ptr(_insn_name).to_string_lossy().into_owned()
        };

        Some(insn_name)
    }

    /// Converts a group id `group_id` to a `String` containing the group name.
    pub fn group_name(&self, group_id: InsnGroupId) -> Option<String> {
        let group_name = unsafe {
            let _group_name = cs_group_name(self.csh(), c_uint::from(group_id.0));
            if _group_name.is_null() {
                return None;
            }

            CStr::from_ptr(_group_name).to_string_lossy().into_owned()
        };

        Some(group_name)
    }

    /// Returns `Detail` structure for a given instruction
    ///
    /// Requires:
    ///
    /// 1. Instruction was created with detail enabled
    /// 2. Skipdata is disabled
    /// 3. Capstone was not compiled in diet mode
    pub fn insn_detail<'s, 'i: 's>(&'s self, insn: &'i Insn) -> CsResult<InsnDetail<'i>> {
        if !self.detail_enabled {
            Err(Error::DetailOff)
        } else if insn.id().0 == 0 {
            Err(Error::IrrelevantDataInSkipData)
        } else if Self::is_diet() {
            Err(Error::IrrelevantDataInDiet)
        } else {
            Ok(unsafe { insn.detail(self.arch) })
        }
    }

    /// Returns a tuple (major, minor) indicating the version of the capstone C library.
    pub fn lib_version() -> (u32, u32) {
        let mut major: c_int = 0;
        let mut minor: c_int = 0;
        let major_ptr: *mut c_int = &mut major;
        let minor_ptr: *mut c_int = &mut minor;

        // We can ignore the "hexical" version returned by capstone because we already have the
        // major and minor versions
        let _ = unsafe { cs_version(major_ptr, minor_ptr) };

        (major as u32, minor as u32)
    }

    /// Returns whether the capstone library supports a given architecture.
    pub fn supports_arch(arch: Arch) -> bool {
        unsafe { cs_support(arch as c_int) }
    }

    /// Returns whether the capstone library was compiled in diet mode.
    pub fn is_diet() -> bool {
        unsafe { cs_support(CS_SUPPORT_DIET as c_int) }
    }
}

impl<'cs> Drop for Capstone<'cs> {
    fn drop(&mut self) {
        unsafe { cs_close(&mut self.csh()) };
    }
}
<|endoftext|>


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

use std::collections::BTreeMap;
use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use dbus::{Connection, NameFlag};
use dbus::tree::{Factory, Tree, Property, MethodFn, MethodErr, EmitsChangedSignal};
use dbus::MessageItem;

use froyo::Froyo;
use blockdev::BlockMember;
use types::FroyoResult;

#[derive(Debug, Clone)]
pub struct DbusContext<'a> {
    name_prop: Arc<Property<MethodFn<'a>>>,
    pub remaining_prop: Arc<Property<MethodFn<'a>>>,
    pub total_prop: Arc<Property<MethodFn<'a>>>,
    pub status_prop: Arc<Property<MethodFn<'a>>>,
    pub running_status_prop: Arc<Property<MethodFn<'a>>>,
    pub block_devices_prop: Arc<Property<MethodFn<'a>>>,
}

impl<'a> DbusContext<'a> {
    pub fn update_one(prop: &Arc<Property<MethodFn<'a>>>, m: MessageItem)
                      -> FroyoResult<()> {
        prop.set_value(m);
        // TODO: result is signals we need to be sending for PropertyChanged???
        Ok(())
    }

    pub fn get_block_devices_msgitem(block_devs: &BTreeMap<String, BlockMember>)
                                     -> MessageItem {
        let mut msg_vec = Vec::new();
        for bd in block_devs.values() {
            let (bd_path, bd_status) = match *bd {
                BlockMember::Present(ref bd) =>
                // TODO: in-use vs not-in-use
                    (RefCell::borrow(&bd).path.to_string_lossy().into_owned(), 0u32),
                BlockMember::Absent(ref sbd) =>
                    (sbd.path.to_string_lossy().into_owned(), 3u32),
            };

            let entry = MessageItem::Struct(vec![bd_path.into(), bd_status.into()]);
            msg_vec.push(entry);
        }

        MessageItem::new_array(msg_vec)
            .expect("Froyodev with no blockdev members???")
    }
}

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo<'a>>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let mut froyos = RefCell::borrow_mut(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::new(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&"Froyo create failed")),
        };
        try!(froyo.save_state()
             .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter_mut()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let name_p = iface.add_p_ref(f.property(
                "Name", RefCell::borrow(&*froyo).name.to_owned()));
            let p_closed_over = name_p.clone();
            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    froyo.name = name.clone();
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

                    try!(p_closed_over.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let rem_p = iface.add_p_ref(f.property("RemainingSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let tot_p = iface.add_p_ref(f.property("TotalSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let status_p = iface.add_p_ref(f.property("Status", 0u32));
            let running_status_p = iface.add_p_ref(f.property("RunningStatus", 0u32));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("AddBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 2 {
                        return Err(MethodErr::no_arg())
                    }

                    let force: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let new_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.add_block_device(Path::new(&new_dev), force)
                        .map_err(|_| MethodErr::failed(&"Adding block device failed")));
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_device_path", "s"))
                    .in_arg(("force", "b")));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("RemoveBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let removing_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.remove_block_device(Path::new(&removing_dev))
                        .map_err(|_| MethodErr::failed(&"Removing block device failed")));
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_device_path", "s")));

            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("Reshape", move |m,_,_| {
                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.reshape()
                        .map_err(|_| MethodErr::failed(&"Reshape failed")));
                    Ok(vec![m.method_return()])
                }));

            let mut froyo = RefCell::borrow_mut(froyo);

            // Need to actually get values b/c I can't figure out how to
            // get a 0-length array of struct
            let bdev_msg = DbusContext::get_block_devices_msgitem(&froyo.block_devs);
            let block_devices_p = iface.add_p_ref(f.property("BlockDevices", bdev_msg));

            let path = format!("/org/freedesktop/froyo/{}", froyo.id);
            froyo.dbus_context = Some(DbusContext {
                name_prop: name_p,
                remaining_prop: rem_p,
                total_prop: tot_p,
                status_prop: status_p,
                running_status_prop: running_status_p,
                block_devices_prop: block_devices_p,
            });

            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    for froyo in &*froyos {
        try!(RefCell::borrow(&froyo).update_dbus());
    }

    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}
<commit_msg>dbus: Change two arg names<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::BTreeMap;
use std::borrow::Borrow;
use std::rc::Rc;
use std::cell::RefCell;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use dbus::{Connection, NameFlag};
use dbus::tree::{Factory, Tree, Property, MethodFn, MethodErr, EmitsChangedSignal};
use dbus::MessageItem;

use froyo::Froyo;
use blockdev::BlockMember;
use types::FroyoResult;

#[derive(Debug, Clone)]
pub struct DbusContext<'a> {
    name_prop: Arc<Property<MethodFn<'a>>>,
    pub remaining_prop: Arc<Property<MethodFn<'a>>>,
    pub total_prop: Arc<Property<MethodFn<'a>>>,
    pub status_prop: Arc<Property<MethodFn<'a>>>,
    pub running_status_prop: Arc<Property<MethodFn<'a>>>,
    pub block_devices_prop: Arc<Property<MethodFn<'a>>>,
}

impl<'a> DbusContext<'a> {
    pub fn update_one(prop: &Arc<Property<MethodFn<'a>>>, m: MessageItem)
                      -> FroyoResult<()> {
        prop.set_value(m);
        // TODO: result is signals we need to be sending for PropertyChanged???
        Ok(())
    }

    pub fn get_block_devices_msgitem(block_devs: &BTreeMap<String, BlockMember>)
                                     -> MessageItem {
        let mut msg_vec = Vec::new();
        for bd in block_devs.values() {
            let (bd_path, bd_status) = match *bd {
                BlockMember::Present(ref bd) =>
                // TODO: in-use vs not-in-use
                    (RefCell::borrow(&bd).path.to_string_lossy().into_owned(), 0u32),
                BlockMember::Absent(ref sbd) =>
                    (sbd.path.to_string_lossy().into_owned(), 3u32),
            };

            let entry = MessageItem::Struct(vec![bd_path.into(), bd_status.into()]);
            msg_vec.push(entry);
        }

        MessageItem::new_array(msg_vec)
            .expect("Froyodev with no blockdev members???")
    }
}

pub fn get_tree<'a>(c: &Connection, froyos: &mut Rc<RefCell<Vec<Rc<RefCell<Froyo<'a>>>>>>)
                       -> FroyoResult<Tree<MethodFn<'a>>> {
    c.register_name("org.freedesktop.Froyo1", NameFlag::ReplaceExisting as u32).unwrap();

    let f = Factory::new_fn();

    let froyos_closed_over = froyos.clone();
    let mut froyos = RefCell::borrow_mut(&froyos);

    let create_method = f.method("Create", move |m,_,_| {
        let mut items = m.get_items();
        if items.len() < 3 {
            return Err(MethodErr::no_arg())
        }

        let force: bool = try!(items.pop().ok_or_else(MethodErr::no_arg)
                               .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));
        let blockdevs = match try!(items.pop().ok_or_else(MethodErr::no_arg)) {
            MessageItem::Array(x, _) => x,
            x => return Err(MethodErr::invalid_arg(&x)),
        };
        let blockdevs = blockdevs.into_iter()
            .map(|x| PathBuf::from(x.inner::<&str>().unwrap()))
            .collect::<Vec<_>>();

        let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                        .and_then(|i| i.inner::<&str>()
                                  .map_err(|_| MethodErr::invalid_arg(&i))
                                  .map(|i| i.to_owned())));

        let froyo = match Froyo::new(&name, &blockdevs, force) {
            Ok(x) => x,
            Err(_) => return Err(MethodErr::failed(&"Froyo create failed")),
        };
        try!(froyo.save_state()
             .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

        let s = format!("/org/freedesktop/froyo/{}", froyo.id);
        let mr = m.method_return().append(s);

        RefCell::borrow_mut(&froyos_closed_over).push(Rc::new(RefCell::new(froyo)));

        Ok(vec![mr])
    })
        .in_arg(("name", "s"))
        .in_arg(("blockdevs", "as"))
        .in_arg(("force", "b"))
        .out_arg(("obj_path", "s"));

    let tree = f.tree();
    let tree = tree
        .add(f.object_path("/org/freedesktop/froyo")
             .introspectable()
             .object_manager()
             .add(f.interface("org.freedesktop.FroyoService1")
                  .add_m(create_method)
             ));

    let tree = froyos
        .iter_mut()
        .fold(tree, |tree, froyo| {

            let mut iface = f.interface("org.freedesktop.FroyoDevice1");
            let name_p = iface.add_p_ref(f.property(
                "Name", RefCell::borrow(&*froyo).name.to_owned()));
            let p_closed_over = name_p.clone();
            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("SetName", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let name = try!(items.pop().ok_or_else(MethodErr::no_arg)
                                    .and_then(|i| i.inner::<&str>()
                                              .map_err(|_| MethodErr::invalid_arg(&i))
                                              .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    froyo.name = name.clone();
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));

                    try!(p_closed_over.set_value(name.into())
                         .map_err(|_| MethodErr::invalid_arg(&"name")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("new_name", "s")));

            let rem_p = iface.add_p_ref(f.property("RemainingSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let tot_p = iface.add_p_ref(f.property("TotalSectors", 0u64)
                                        .emits_changed(EmitsChangedSignal::False));
            let status_p = iface.add_p_ref(f.property("Status", 0u32));
            let running_status_p = iface.add_p_ref(f.property("RunningStatus", 0u32));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("AddBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 2 {
                        return Err(MethodErr::no_arg())
                    }

                    let force: bool = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(
                                |i| i.inner().map_err(|_| MethodErr::invalid_arg(&i))));

                    let new_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.add_block_device(Path::new(&new_dev), force)
                        .map_err(|_| MethodErr::failed(&"Adding block device failed")));
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("device_path", "s"))
                    .in_arg(("force", "b")));

            let froyo_closed_over = froyo.clone();
            let iface = iface.add_m(
                f.method("RemoveBlockDevice", move |m,_,_| {
                    let mut items = m.get_items();
                    if items.len() < 1 {
                        return Err(MethodErr::no_arg())
                    }

                    let removing_dev = try!(
                        items.pop()
                            .ok_or_else(MethodErr::no_arg)
                            .and_then(|i| i.inner::<&str>()
                                      .map_err(|_| MethodErr::invalid_arg(&i))
                                      .map(|i| i.to_owned())));

                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.remove_block_device(Path::new(&removing_dev))
                        .map_err(|_| MethodErr::failed(&"Removing block device failed")));
                    try!(froyo.save_state()
                         .map_err(|_| MethodErr::failed(&"Froyo saving state failed")));
                    Ok(vec![m.method_return()])
                })
                    .in_arg(("device_path", "s")));

            let froyo_closed_over = froyo.clone();
            let mut iface = iface.add_m(
                f.method("Reshape", move |m,_,_| {
                    let mut froyo = RefCell::borrow_mut(&*froyo_closed_over);
                    try!(froyo.reshape()
                        .map_err(|_| MethodErr::failed(&"Reshape failed")));
                    Ok(vec![m.method_return()])
                }));

            let mut froyo = RefCell::borrow_mut(froyo);

            // Need to actually get values b/c I can't figure out how to
            // get a 0-length array of struct
            let bdev_msg = DbusContext::get_block_devices_msgitem(&froyo.block_devs);
            let block_devices_p = iface.add_p_ref(f.property("BlockDevices", bdev_msg));

            let path = format!("/org/freedesktop/froyo/{}", froyo.id);
            froyo.dbus_context = Some(DbusContext {
                name_prop: name_p,
                remaining_prop: rem_p,
                total_prop: tot_p,
                status_prop: status_p,
                running_status_prop: running_status_p,
                block_devices_prop: block_devices_p,
            });

            tree.add(f.object_path(path)
                     .introspectable()
                     .add(iface))
        });

    for froyo in &*froyos {
        try!(RefCell::borrow(&froyo).update_dbus());
    }

    tree.set_registered(&c, true).unwrap();

    Ok(tree)
}
<|endoftext|>


<commit_before>use hash::{calculate_sha1, Sha1};
use std::{convert, io};
use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, Write};
use std::path::Path;
use std::sync::mpsc::{Sender, SendError};

use ipc::IPC;
use metainfo::Metainfo;
use request_metadata::RequestMetadata;
use request_queue::RequestQueue;

pub const BLOCK_SIZE: u32 = 16384;

pub struct Download {
    pub our_peer_id: String,
    pub metainfo:    Metainfo,
    pieces:          Vec<Piece>,
    file:            File,
    peer_channels:   Vec<Sender<IPC>>,
}

impl Download {
    pub fn new(our_peer_id: String, metainfo: Metainfo) -> Result<Download, Error> {
        let file_length = metainfo.info.length;
        let piece_length = metainfo.info.piece_length;
        let num_pieces = metainfo.info.num_pieces;

        // create/open file
        let path = Path::new("downloads").join(&metainfo.info.name);
        let mut file = try!(OpenOptions::new().create(true).read(true).write(true).open(path));

        // create pieces
        let mut pieces = vec![];
        for i in 0..num_pieces {
            let len = if i < (num_pieces - 1) {
                piece_length
            } else {
                (file_length - (piece_length as u64 * (num_pieces as u64 - 1))) as u32
            };
            let piece = try!(Piece::new(i, len, piece_length, metainfo.info.pieces[i as usize].clone(), &mut file));
            pieces.push(piece);
        }

        Ok(Download {
            our_peer_id:   our_peer_id,
            metainfo:      metainfo,
            pieces:        pieces,
            file:          file,
            peer_channels: vec![],
        })
    }

    pub fn register_peer(&mut self, channel: Sender<IPC>) {
        self.peer_channels.push(channel);
    }

    pub fn store(&mut self, piece_index: u32, block_index: u32, data: Vec<u8>) -> Result<(), Error> {
        {
            let piece = &mut self.pieces[piece_index as usize];
            if piece.is_complete || piece.has_block(block_index) {
                // if we already have this block, do an early return to avoid re-writing the piece, sending complete messages, etc
                return Ok(())
            }
            try!(piece.store(&mut self.file, block_index, data));
        }

        // notify peers that this block is complete
        self.broadcast(IPC::BlockComplete(piece_index, block_index));

        // notify peers if piece is complete
        if self.pieces[piece_index as usize].is_complete {
            self.broadcast(IPC::PieceComplete(piece_index));
        }

        // notify peers if download is complete
        if self.is_complete() {
            println!("Download complete");
            self.broadcast(IPC::DownloadComplete);
        }

        Ok(())
    }

    pub fn retrive_data(&mut self, request: &RequestMetadata) -> Result<Vec<u8>, Error> {
        let ref piece = self.pieces[request.piece_index as usize];
        if piece.is_complete {
            let offset = (piece.index as u64 * piece.piece_length as u64) + request.offset as u64;
            let file = &mut self.file;
            try!(file.seek(io::SeekFrom::Start(offset)));
            let mut buf = vec![];
            try!(file.take(request.block_length as u64).read_to_end(&mut buf));
            Ok(buf)
        } else {
            Err(Error::MissingPieceData)
        }

    }

    pub fn is_interested(&self, peer_has_pieces: &[bool]) -> bool {
        for piece in self.pieces.iter() {
            if !piece.is_complete && peer_has_pieces[piece.index as usize] {
                return true;
            }
        }
        false
    }

    pub fn incomplete_blocks_of_interest(&self, peer_has_pieces: &[bool], request_queue: &RequestQueue) -> Vec<(u32, u32, u32)> {
        let mut out = vec![];
        for piece in self.pieces.iter() {
            if !piece.is_complete && peer_has_pieces[piece.index as usize] {
                for block in piece.blocks.iter() {
                    if block.data.is_none() && !request_queue.has(piece.index, block.index) {
                        out.push((piece.index, block.index, block.length));
                    }
                }
            }
        }
        out
    }

    pub fn have_pieces(&self) -> Vec<bool> {
        self.pieces.iter().map(|p| p.is_complete).collect()
    }

    fn is_complete(&self) -> bool {
        for piece in self.pieces.iter() {
            if !piece.is_complete {
                return false
            }
        }
        true
    }

    fn broadcast(&mut self, ipc: IPC) {
        self.peer_channels.retain(|channel| {
            match channel.send(ipc.clone()) {
                Ok(_) => true,
                Err(SendError(_)) => false // presumably channel has disconnected
            }
        });
    }
}

struct Piece {
    index:        u32,
    piece_length: u32,
    hash:         Sha1,
    blocks:       Vec<Block>,
    is_complete:  bool,
}

impl Piece {
    fn new(index: u32, length: u32, piece_length: u32, hash: Sha1, file: &mut File) -> Result<Piece, Error> {
        // create blocks
        let mut blocks = vec![];
        let num_blocks = (length as f64 / BLOCK_SIZE as f64).ceil() as u32;
        for i in 0..num_blocks {
            let len = if i < (num_blocks - 1) {
                BLOCK_SIZE
            } else {
                length - (BLOCK_SIZE * (num_blocks - 1))
            };
            blocks.push(Block::new(i, len));
        }

        // check if this piece is already complete
        let offset = index as u64 * piece_length as u64;
        try!(file.seek(io::SeekFrom::Start(offset)));
        let mut buf = vec![];
        try!(file.take(length as u64).read_to_end(&mut buf));
        let is_complete = hash == calculate_sha1(&buf);

        Ok(Piece {
            index:        index,
            piece_length: piece_length,
            hash:         hash,
            blocks:       blocks,
            is_complete:  is_complete,
        })
    }

    fn store(&mut self, file: &mut File, block_index: u32, data: Vec<u8>) -> Result<(), Error> {
        {
            // store data in the appropriate block
            let block = &mut self.blocks[block_index as usize];
            block.data = Some(data);
        }

        if self.have_all_blocks() {
            // concatenate data from blocks together
            let mut data = vec![];
            for block in self.blocks.iter() {
                data.extend(block.data.clone().unwrap());
            }

            // validate that piece data matches SHA1 hash
            println!("Have {} bytes of data for index {}", data.len(), self.index);
            if self.hash == calculate_sha1(&data) {
                println!("Piece {} is complete and correct, writing to the file.", self.index);
                let offset = self.index as u64 * self.piece_length as u64;
                try!(file.seek(io::SeekFrom::Start(offset)));
                try!(file.write_all(&data));
                self.clear_block_data();
                self.is_complete = true;
            } else {
                println!("Piece is corrupt, deleting downloaded piece data!");
                self.clear_block_data();
            }
        }
        Ok(())
    }

    fn has_block(&self, block_index: u32) -> bool {
        self.blocks[block_index as usize].data.is_some()
    }

    fn have_all_blocks(&self) -> bool {
        for block in self.blocks.iter() {
            if block.data.is_none() {
                return false
            }
        }
        true
    }

    fn clear_block_data(&mut self) {
        for block in self.blocks.iter_mut() {
            block.data = None;
        }
    }
}

struct Block {
    index:  u32,
    length: u32,
    data:   Option<Vec<u8>>,
}

impl Block {
    fn new(index: u32, length: u32) -> Block {
        Block {
            index:  index,
            length: length,
            data:   None,
        }
    }
}

#[derive(Debug)]
pub enum Error {
    MissingPieceData,
    IoError(io::Error),
}

impl convert::From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}
<commit_msg>Store offset instead of piece_length in Piece metadata.<commit_after>use hash::{calculate_sha1, Sha1};
use std::{convert, io};
use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, Write};
use std::path::Path;
use std::sync::mpsc::{Sender, SendError};

use ipc::IPC;
use metainfo::Metainfo;
use request_metadata::RequestMetadata;
use request_queue::RequestQueue;

pub const BLOCK_SIZE: u32 = 16384;

pub struct Download {
    pub our_peer_id: String,
    pub metainfo:    Metainfo,
    pieces:          Vec<Piece>,
    file:            File,
    peer_channels:   Vec<Sender<IPC>>,
}

impl Download {
    pub fn new(our_peer_id: String, metainfo: Metainfo) -> Result<Download, Error> {
        let file_length = metainfo.info.length;
        let piece_length = metainfo.info.piece_length;
        let num_pieces = metainfo.info.num_pieces;

        // create/open file
        let path = Path::new("downloads").join(&metainfo.info.name);
        let mut file = try!(OpenOptions::new().create(true).read(true).write(true).open(path));

        // create pieces
        let mut pieces = vec![];
        for i in 0..num_pieces {
            let offset = i as u64 * piece_length as u64;
            let length = if i < (num_pieces - 1) {
                piece_length
            } else {
                (file_length - offset) as u32
            };
            let piece = try!(Piece::new(i, length, offset, metainfo.info.pieces[i as usize].clone(), &mut file));
            pieces.push(piece);
        }

        Ok(Download {
            our_peer_id:   our_peer_id,
            metainfo:      metainfo,
            pieces:        pieces,
            file:          file,
            peer_channels: vec![],
        })
    }

    pub fn register_peer(&mut self, channel: Sender<IPC>) {
        self.peer_channels.push(channel);
    }

    pub fn store(&mut self, piece_index: u32, block_index: u32, data: Vec<u8>) -> Result<(), Error> {
        {
            let piece = &mut self.pieces[piece_index as usize];
            if piece.is_complete || piece.has_block(block_index) {
                // if we already have this block, do an early return to avoid re-writing the piece, sending complete messages, etc
                return Ok(())
            }
            try!(piece.store(&mut self.file, block_index, data));
        }

        // notify peers that this block is complete
        self.broadcast(IPC::BlockComplete(piece_index, block_index));

        // notify peers if piece is complete
        if self.pieces[piece_index as usize].is_complete {
            self.broadcast(IPC::PieceComplete(piece_index));
        }

        // notify peers if download is complete
        if self.is_complete() {
            println!("Download complete");
            self.broadcast(IPC::DownloadComplete);
        }

        Ok(())
    }

    pub fn retrive_data(&mut self, request: &RequestMetadata) -> Result<Vec<u8>, Error> {
        let ref piece = self.pieces[request.piece_index as usize];
        if piece.is_complete {
            let offset = piece.offset + request.offset as u64;
            let file = &mut self.file;
            try!(file.seek(io::SeekFrom::Start(offset)));
            let mut buf = vec![];
            try!(file.take(request.block_length as u64).read_to_end(&mut buf));
            Ok(buf)
        } else {
            Err(Error::MissingPieceData)
        }

    }

    pub fn is_interested(&self, peer_has_pieces: &[bool]) -> bool {
        for piece in self.pieces.iter() {
            if !piece.is_complete && peer_has_pieces[piece.index as usize] {
                return true;
            }
        }
        false
    }

    pub fn incomplete_blocks_of_interest(&self, peer_has_pieces: &[bool], request_queue: &RequestQueue) -> Vec<(u32, u32, u32)> {
        let mut out = vec![];
        for piece in self.pieces.iter() {
            if !piece.is_complete && peer_has_pieces[piece.index as usize] {
                for block in piece.blocks.iter() {
                    if block.data.is_none() && !request_queue.has(piece.index, block.index) {
                        out.push((piece.index, block.index, block.length));
                    }
                }
            }
        }
        out
    }

    pub fn have_pieces(&self) -> Vec<bool> {
        self.pieces.iter().map(|p| p.is_complete).collect()
    }

    fn is_complete(&self) -> bool {
        for piece in self.pieces.iter() {
            if !piece.is_complete {
                return false
            }
        }
        true
    }

    fn broadcast(&mut self, ipc: IPC) {
        self.peer_channels.retain(|channel| {
            match channel.send(ipc.clone()) {
                Ok(_) => true,
                Err(SendError(_)) => false // presumably channel has disconnected
            }
        });
    }
}

struct Piece {
    index:       u32,
    offset:      u64,
    hash:        Sha1,
    blocks:      Vec<Block>,
    is_complete: bool,
}

impl Piece {
    fn new(index: u32, length: u32, offset: u64, hash: Sha1, file: &mut File) -> Result<Piece, Error> {
        // create blocks
        let mut blocks = vec![];
        let num_blocks = (length as f64 / BLOCK_SIZE as f64).ceil() as u32;
        for i in 0..num_blocks {
            let len = if i < (num_blocks - 1) {
                BLOCK_SIZE
            } else {
                length - (BLOCK_SIZE * (num_blocks - 1))
            };
            blocks.push(Block::new(i, len));
        }

        // check if this piece is already complete
        try!(file.seek(io::SeekFrom::Start(offset)));
        let mut buf = vec![];
        try!(file.take(length as u64).read_to_end(&mut buf));
        let is_complete = hash == calculate_sha1(&buf);

        Ok(Piece {
            index:       index,
            offset:      offset,
            hash:        hash,
            blocks:      blocks,
            is_complete: is_complete,
        })
    }

    fn store(&mut self, file: &mut File, block_index: u32, data: Vec<u8>) -> Result<(), Error> {
        {
            // store data in the appropriate block
            let block = &mut self.blocks[block_index as usize];
            block.data = Some(data);
        }

        if self.have_all_blocks() {
            // concatenate data from blocks together
            let mut data = vec![];
            for block in self.blocks.iter() {
                data.extend(block.data.clone().unwrap());
            }

            // validate that piece data matches SHA1 hash
            println!("Have {} bytes of data for index {}", data.len(), self.index);
            if self.hash == calculate_sha1(&data) {
                println!("Piece {} is complete and correct, writing to the file.", self.index);
                try!(file.seek(io::SeekFrom::Start(self.offset)));
                try!(file.write_all(&data));
                self.clear_block_data();
                self.is_complete = true;
            } else {
                println!("Piece is corrupt, deleting downloaded piece data!");
                self.clear_block_data();
            }
        }
        Ok(())
    }

    fn has_block(&self, block_index: u32) -> bool {
        self.blocks[block_index as usize].data.is_some()
    }

    fn have_all_blocks(&self) -> bool {
        for block in self.blocks.iter() {
            if block.data.is_none() {
                return false
            }
        }
        true
    }

    fn clear_block_data(&mut self) {
        for block in self.blocks.iter_mut() {
            block.data = None;
        }
    }
}

struct Block {
    index:  u32,
    length: u32,
    data:   Option<Vec<u8>>,
}

impl Block {
    fn new(index: u32, length: u32) -> Block {
        Block {
            index:  index,
            length: length,
            data:   None,
        }
    }
}

#[derive(Debug)]
pub enum Error {
    MissingPieceData,
    IoError(io::Error),
}

impl convert::From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
# ears

__ears__ is a simple library for play Audio in Rust.

__ears__ is build on the top of OpenAL and libsndfile.

* Provide an acces to the OpenAL spatialization functionnality in a simple way.
* Accept a lot of audio format thanks to libsndfile.

# A simple example

```Rust
extern mod ears;
use ears::Sound;

fn main() -> {
	// Create a new Sound.
	let snd = Sound::new(~"path/to/my/sound.ogg").unwrap();

	// Play the Sound
	snd.play();

	// Wait until the end of the sound
	while snd.is_playing() {}
}
```

# Functionnality

__ears__ provide two way for play audio files.

* The Sound class, which represent light sounds who can share a buffer of samples with another Sound.
* The Music class, which is a bigger sound and who can't share sample buffer.

# Use ears

As said before, __ears__ require OpenAL and libsndfile, you need to install these two libraries in your system.

__ears__ compile against the last Rust compiler, so if it doesn't work on your computer you may need to update your compiler.

__ears__ work fully with `rustpkg`, so just install __ears__ in your project workspace like this :

```Shell
> rustpkg install github.com/JeremyLetang/ears
```

then import stuff from __ears__ in your project, you can import all the stuff :

```Rust
extern mod ears;

use ears::*;
```

or a specific :

```Rust
extern mod ears;

use ears::Music;
```

# Compile examples

Like __ears__ you can build the examples with `rustpkg`, just do :

```Shell
> rustpkg install examples/an_example
```
*/

#[feature(globs, macro_rules, managed_boxes)];

#[crate_id = "ears#0.1"]
#[desc = "Easy Api in Rust for Sounds"];
#[license = "MIT"];
#[crate_type = "lib"];
// #[warn(missing_doc)];
#[allow(dead_code)];

extern mod extra;

// Reexport public API
pub use init::ears_init;
pub use music::Music;
pub use sound::Sound;
pub use states::{State, Initial, Playing, Paused, Stopped};
pub use sound_data::SoundData;
pub use audio_controller::AudioController;
pub use audio_tags::{AudioTags, Tags};

// Hidden internal bindings
#[doc(hidden)]
pub mod internal;
#[doc(hidden)]
mod openal;
#[doc(hidden)]
mod sndfile;

// The public ears API
pub mod init;
pub mod listener;
pub mod sound;
pub mod music;
pub mod sound_data;
pub mod states;
pub mod audio_controller;
pub mod audio_tags;
pub mod recorder;
<commit_msg>fix crate_id<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/*!
# ears

__ears__ is a simple library for play Audio in Rust.

__ears__ is build on the top of OpenAL and libsndfile.

* Provide an acces to the OpenAL spatialization functionnality in a simple way.
* Accept a lot of audio format thanks to libsndfile.

# A simple example

```Rust
extern mod ears;
use ears::Sound;

fn main() -> {
	// Create a new Sound.
	let snd = Sound::new(~"path/to/my/sound.ogg").unwrap();

	// Play the Sound
	snd.play();

	// Wait until the end of the sound
	while snd.is_playing() {}
}
```

# Functionnality

__ears__ provide two way for play audio files.

* The Sound class, which represent light sounds who can share a buffer of samples with another Sound.
* The Music class, which is a bigger sound and who can't share sample buffer.

# Use ears

As said before, __ears__ require OpenAL and libsndfile, you need to install these two libraries in your system.

__ears__ compile against the last Rust compiler, so if it doesn't work on your computer you may need to update your compiler.

__ears__ work fully with `rustpkg`, so just install __ears__ in your project workspace like this :

```Shell
> rustpkg install github.com/JeremyLetang/ears
```

then import stuff from __ears__ in your project, you can import all the stuff :

```Rust
extern mod ears;

use ears::*;
```

or a specific :

```Rust
extern mod ears;

use ears::Music;
```

# Compile examples

Like __ears__ you can build the examples with `rustpkg`, just do :

```Shell
> rustpkg install examples/an_example
```
*/

#[feature(globs, macro_rules, managed_boxes)];

#[crate_id = "ears#0.1"];
#[desc = "Easy Api in Rust for Sounds"];
#[license = "MIT"];
#[crate_type = "lib"];
// #[warn(missing_doc)];
#[allow(dead_code)];

extern mod extra;

// Reexport public API
pub use init::ears_init;
pub use music::Music;
pub use sound::Sound;
pub use states::{State, Initial, Playing, Paused, Stopped};
pub use sound_data::SoundData;
pub use audio_controller::AudioController;
pub use audio_tags::{AudioTags, Tags};

// Hidden internal bindings
#[doc(hidden)]
pub mod internal;
#[doc(hidden)]
mod openal;
#[doc(hidden)]
mod sndfile;

// The public ears API
pub mod init;
pub mod listener;
pub mod sound;
pub mod music;
pub mod sound_data;
pub mod states;
pub mod audio_controller;
pub mod audio_tags;
pub mod recorder;
<|endoftext|>


<commit_before>// Copyright (c) 2015, Sam Payson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TODO: Remove this when things stabilize.
#![allow(dead_code)]

use encoding::Quantifier::*;

#[derive(Copy,Eq,PartialEq)]
pub enum Type {
	Int8  = 0,
	Int16 = 1,
	Int32 = 2,
	Int64 = 3,

	UInt8  = 4,
	UInt16 = 5,
	UInt32 = 6,
	UInt64 = 7,

	Fixed32 = 8,
	Fixed64 = 9,

	Float32 = 10,
	Float64 = 11,

	Bytes  = 12,
	String = 13,

	Bool = 14,

	Enum = 15,

	FirstUnused = 16,
}

// The Quantifier type gives the multiplicity of a field. A Required field has exactly 1 element, an
// Optional field has 0 or 1 elements, and a Repeated field has 0 or more elements.
#[derive(Debug,Copy,PartialEq,Eq)]
pub enum Quantifier {
	Required = 0,
	Optional = 1,
	Repeated = 2,
}

#[derive(PartialEq,Eq)]
pub struct FieldEncoding {
	// Integer id of this field within its containing record.
	pub id: u64,

	// Name of this field in the .rex file, not used in the encoding.
	pub name: String,

	// Is this field Required, Optional (opt), or Repeated (rep)?
	pub quant: Quantifier,

	// Type of this field.
	pub typ: usize,

    // The bounds field is the product of all bounds in an array field. So for example, the field
    //
    //     1 matrix : [3][3]float32
    //
    // would have a bounds field of 3*3 = 9.
    //
    // The bounds field is not present for non-array types.
	pub bounds: Option<usize>,
}

#[derive(PartialEq,Eq)]
pub struct RecordEncoding {
	// Name of the record type in the .rex file, not used in the encoding.
	pub name: String,

	// Required fields of this record type, sorted by id.
	pub req_fields: Vec<FieldEncoding>,

	// Optional and repeated fields of this record type, sorted by id.
	pub opt_rep_fields: Vec<FieldEncoding>,
}

impl RecordEncoding {
	fn sort_fields(&mut self) {
		self.req_fields.sort_by(|a, b| a.id.cmp(&b.id));
		self.opt_rep_fields.sort_by(|a, b| a.id.cmp(&b.id));
	}
}

// A CompleteEncoding provides all of the information necessary to parse a particular record type
// (and every record type that it can contain).
#[derive(PartialEq,Eq)]
pub struct CompleteEncoding {
	// The record type that this CompleteEncoding describes.
	pub target:  RecordEncoding,

	// Encodings for all dependencies of target. If a field has a type (t >= Type::FirstUnused),
	// then a RecordEncoding for that type is at depends[t - Type::FirstUnused].
	pub depends: Vec<RecordEncoding>,
}

impl CompleteEncoding {
	pub fn sort_fields(&mut self) {
		self.target.sort_fields();

		for dep in self.depends.iter_mut() {
			dep.sort_fields();
		}
	}
}

// These are indices into COMPLETE_ENC.depends, below. See docs for that field on the
// CompleteEncoding type.
const FIELD_ENCODING_TYP:  usize = 0 + (Type::FirstUnused as usize);
const RECORD_ENCODING_TYP: usize = 1 + (Type::FirstUnused as usize);

lazy_static! {

	// I apologize in advance for the confusing-ness of this comment.
	//
	// Encodings for records are themselves encoded, so we need to solve the chicken/egg problem in
	// order to be able to interpret the encodings of encodings :3.
	//
	// We do this by providing a pre-decoded encoding for encodings. That's what this lovely
	// structure is.
	pub static ref COMPLETE_ENC: CompleteEncoding = CompleteEncoding {
		target: RecordEncoding {
			name: "CompleteEncoding".to_string(),
			req_fields: vec![

				FieldEncoding {
					id:     1,
					name:   "target".to_string(),
					quant:  Required,
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],

			opt_rep_fields: vec![

				FieldEncoding {
					id:     2,
					quant:  Repeated,
					name:   "depends".to_string(),
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],
		},

		depends: vec![

			RecordEncoding {
				name: "FieldEncoding".to_string(),
				req_fields: vec![

					FieldEncoding {
						id:     1,
						name:   "id".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},

					FieldEncoding {
						id:     2,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "quant".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     4,
						name:   "typ".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     5,
						name:   "bounds".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},
				],
				opt_rep_fields: vec![]
			},

			RecordEncoding {
				name: "RecordEncoding".to_string(),
				req_fields: vec![
					FieldEncoding {
						id:     1,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},
				],

				opt_rep_fields: vec![
					FieldEncoding {
						id:     2,
						name:   "req_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "opt_rep_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},
				],
			},
		],
	};
}
<commit_msg>Added a method to convert usize to Type<commit_after>// Copyright (c) 2015, Sam Payson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TODO: Remove this when things stabilize.
#![allow(dead_code)]

use encoding::Quantifier::*;

#[derive(Copy,Eq,PartialEq)]
pub enum Type {
	Int8  = 0,
	Int16 = 1,
	Int32 = 2,
	Int64 = 3,

	UInt8  = 4,
	UInt16 = 5,
	UInt32 = 6,
	UInt64 = 7,

	Fixed32 = 8,
	Fixed64 = 9,

	Float32 = 10,
	Float64 = 11,

	Bytes  = 12,
	String = 13,

	Bool = 14,

	Enum = 15,

	FirstUnused = 16,
}

struct NoSuchType;

impl Type {
	pub fn from_usize(x: usize) -> Result<Type, NoSuchType> {
		match x {
			0 => Ok(Type::Int8),
			1 => Ok(Type::Int16),
			2 => Ok(Type::Int32),
			3 => Ok(Type::Int64),

			4 => Ok(Type::UInt8),
			5 => Ok(Type::UInt16),
			6 => Ok(Type::UInt32),
			7 => Ok(Type::UInt64),

			8 => Ok(Type::Fixed32),
			9 => Ok(Type::Fixed64),

			10 => Ok(Type::Float32),
			11 => Ok(Type::Float64),

			12 => Ok(Type::Bytes),
			13 => Ok(Type::String),

			14 => Ok(Type::Bool),

			15 => Ok(Type::Enum),

			_ => Err(NoSuchType),
		}
	}
}

// The Quantifier type gives the multiplicity of a field. A Required field has exactly 1 element, an
// Optional field has 0 or 1 elements, and a Repeated field has 0 or more elements.
#[derive(Debug,Copy,PartialEq,Eq)]
pub enum Quantifier {
	Required = 0,
	Optional = 1,
	Repeated = 2,
}

#[derive(PartialEq,Eq)]
pub struct FieldEncoding {
	// Integer id of this field within its containing record.
	pub id: u64,

	// Name of this field in the .rex file, not used in the encoding.
	pub name: String,

	// Is this field Required, Optional (opt), or Repeated (rep)?
	pub quant: Quantifier,

	// Type of this field.
	pub typ: usize,

    // The bounds field is the product of all bounds in an array field. So for example, the field
    //
    //     1 matrix : [3][3]float32
    //
    // would have a bounds field of 3*3 = 9.
    //
    // The bounds field is not present for non-array types.
	pub bounds: Option<usize>,
}

#[derive(PartialEq,Eq)]
pub struct RecordEncoding {
	// Name of the record type in the .rex file, not used in the encoding.
	pub name: String,

	// Required fields of this record type, sorted by id.
	pub req_fields: Vec<FieldEncoding>,

	// Optional and repeated fields of this record type, sorted by id.
	pub opt_rep_fields: Vec<FieldEncoding>,
}

impl RecordEncoding {
	fn sort_fields(&mut self) {
		self.req_fields.sort_by(|a, b| a.id.cmp(&b.id));
		self.opt_rep_fields.sort_by(|a, b| a.id.cmp(&b.id));
	}
}

// A CompleteEncoding provides all of the information necessary to parse a particular record type
// (and every record type that it can contain).
#[derive(PartialEq,Eq)]
pub struct CompleteEncoding {
	// The record type that this CompleteEncoding describes.
	pub target:  RecordEncoding,

	// Encodings for all dependencies of target. If a field has a type (t >= Type::FirstUnused),
	// then a RecordEncoding for that type is at depends[t - Type::FirstUnused].
	pub depends: Vec<RecordEncoding>,
}

impl CompleteEncoding {
	pub fn sort_fields(&mut self) {
		self.target.sort_fields();

		for dep in self.depends.iter_mut() {
			dep.sort_fields();
		}
	}
}

// These are indices into COMPLETE_ENC.depends, below. See docs for that field on the
// CompleteEncoding type.
const FIELD_ENCODING_TYP:  usize = 0 + (Type::FirstUnused as usize);
const RECORD_ENCODING_TYP: usize = 1 + (Type::FirstUnused as usize);

lazy_static! {

	// I apologize in advance for the confusing-ness of this comment.
	//
	// Encodings for records are themselves encoded, so we need to solve the chicken/egg problem in
	// order to be able to interpret the encodings of encodings :3.
	//
	// We do this by providing a pre-decoded encoding for encodings. That's what this lovely
	// structure is.
	pub static ref COMPLETE_ENC: CompleteEncoding = CompleteEncoding {
		target: RecordEncoding {
			name: "CompleteEncoding".to_string(),
			req_fields: vec![

				FieldEncoding {
					id:     1,
					name:   "target".to_string(),
					quant:  Required,
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],

			opt_rep_fields: vec![

				FieldEncoding {
					id:     2,
					quant:  Repeated,
					name:   "depends".to_string(),
					typ:    RECORD_ENCODING_TYP,
					bounds: None
				},
			],
		},

		depends: vec![

			RecordEncoding {
				name: "FieldEncoding".to_string(),
				req_fields: vec![

					FieldEncoding {
						id:     1,
						name:   "id".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},

					FieldEncoding {
						id:     2,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "quant".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     4,
						name:   "typ".to_string(),
						quant:  Required,
						typ:    Type::Enum as usize,
						bounds: None
					},

					FieldEncoding {
						id:     5,
						name:   "bounds".to_string(),
						quant:  Required,
						typ:    Type::UInt64 as usize,
						bounds: None
					},
				],
				opt_rep_fields: vec![]
			},

			RecordEncoding {
				name: "RecordEncoding".to_string(),
				req_fields: vec![
					FieldEncoding {
						id:     1,
						name:   "name".to_string(),
						quant:  Required,
						typ:    Type::String as usize,
						bounds: None
					},
				],

				opt_rep_fields: vec![
					FieldEncoding {
						id:     2,
						name:   "req_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},

					FieldEncoding {
						id:     3,
						name:   "opt_rep_fields".to_string(),
						quant:  Repeated,
						typ:    FIELD_ENCODING_TYP,
						bounds: None
					},
				],
			},
		],
	};
}
<|endoftext|>