<commit_before>use crate::api::ChannelError::AccessError;
use crate::api::{ChannelError, WriteError, Writer};
use crate::header::Header;
use crate::utils::{align, store_atomic_u64, CLOSE, REC_HEADER_LEN, WATERMARK};
use kekbit_codecs::codecs::DataFormat;
use kekbit_codecs::codecs::Encodable;
use log::{debug, error, info};
use memmap::MmapMut;
use std::cmp::min;
use std::io::Write;
use std::ptr::copy_nonoverlapping;
use std::result::Result;
use std::sync::atomic::Ordering;

/// An implementation of the [Writer](trait.Writer.html) which access a persistent channel through
/// memory mapping, and uses a specific [DataFormat](../codecs/trait.DataFormat.html). A `ShmWriter` must be created using the [shm_writer](fn.shm_writer.html) function.
/// Any `ShmWriter` exclusively holds the channel is bound to, and it is *not thread safe*.
/// If multiple threads must write into a channel they should be externally synchronized.
///
/// # Examples
///
/// ```
/// use kekbit_core::tick::TickUnit::Nanos;
/// use kekbit_core::shm::*;
/// use kekbit_core::header::Header;
/// use kekbit_core::api::Writer;
/// use kekbit_codecs::codecs::raw::RawBinDataFormat;
///
/// const FOREVER: u64 = 99_999_999_999;
/// let writer_id = 1850;
/// let channel_id = 42;
/// let capacity = 3000;
/// let max_msg_len = 100;
/// let header = Header::new(writer_id, channel_id, capacity, max_msg_len, FOREVER, Nanos);
/// let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
/// let mut writer = shm_writer(&test_tmp_dir.path(), &header, RawBinDataFormat).unwrap();
/// writer.heartbeat().unwrap();
/// ```
pub struct ShmWriter<D: DataFormat> {
    header: Header,
    data_ptr: *mut u8,
    write_offset: u32,
    mmap: MmapMut,
    df: D,
    write: KekWrite,
}

impl<D: DataFormat> ShmWriter<D> {
    #[allow(clippy::cast_ptr_alignment)]
    pub(super) fn new(mut mmap: MmapMut, df: D) -> Result<ShmWriter<D>, ChannelError> {
        let buf = &mut mmap[..];
        let header = Header::read(buf)?;
        let header_ptr = buf.as_ptr() as *mut u64;
        let head_len = header.len();
        let data_ptr = unsafe { header_ptr.add(head_len) } as *mut u8;
        let write = KekWrite::new(data_ptr, header.max_msg_len() as usize);
        let mut writer = ShmWriter {
            header,
            data_ptr,
            write_offset: 0,
            mmap,
            df,
            write,
        };
        info!(
            "Kekbit channel writer created. Size is {}MB. Max msg size {}KB",
            writer.header.capacity() / 1_000_000,
            writer.header.max_msg_len() / 1_000
        );
        //sent the very first original heart bear
        match writer.heartbeat() {
            Ok(_) => {
                info!("Initial hearbeat successfully sent!");
                Ok(writer)
            }
            Err(we) => Err(AccessError {
                reason: format!("Initial heartbeat failed!. Reason {:?}", we),
            }),
        }
    }

    #[inline(always)]
    fn write_metadata(&mut self, write_ptr: *mut u64, len: u64, aligned_rec_len: u32) {
        unsafe {
            //we should always have the 8 bytes required by WATERMARK as they are acounted in the Footer
            store_atomic_u64(write_ptr.add(aligned_rec_len as usize), WATERMARK, Ordering::Release);
        }
        store_atomic_u64(write_ptr, len, Ordering::Release);
    }
}

impl<D: DataFormat> Writer<D> for ShmWriter<D> {
    /// Writes a message into the channel. This operation will encode the data directly into  channel.
    /// While this is a non blocking operation, only one write should be executed at any given time.
    ///
    /// Returns the total amount of bytes wrote into the channel which includes, the size of the message,
    /// the size of the message header and the amount of padding add to that message.
    ///
    /// # Arguments
    ///
    /// * `data` - The  data which to encode and  write into the channel.
    ///
    /// # Errors
    ///
    /// Two kinds of [failures](enum.WriteError.html) may occur. One if the encoding operation failed, the other if the channel
    /// rejected the message for reasons such data is too large or no space is available in the channel.
    ///
    /// # Examples
    ///
    /// ```
    /// use kekbit_core::tick::TickUnit::Nanos;
    /// use kekbit_core::shm::*;
    /// use kekbit_core::header::Header;
    /// use kekbit_core::api::Writer;
    /// use kekbit_codecs::codecs::raw::RawBinDataFormat;
    ///
    /// const FOREVER: u64 = 99_999_999_999;
    /// let writer_id = 1850;
    /// let channel_id = 42;
    /// let capacity = 30_000;
    /// let max_msg_len = 100;
    /// let header = Header::new(writer_id, channel_id, capacity, max_msg_len, FOREVER, Nanos);
    /// let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
    /// let mut writer = shm_writer(&test_tmp_dir.path(), &header, RawBinDataFormat).unwrap();
    /// let msg = "There are 10 kinds of people: those who know binary and those who don't";
    /// let msg_data = msg.as_bytes();
    /// writer.write(&msg_data).unwrap();
    /// ```
    ///
    #[allow(clippy::cast_ptr_alignment)]
    //#[allow(clippy::unused_io_amount)]
    fn write(&mut self, data: &impl Encodable<D>) -> Result<u32, WriteError> {
        let read_head_ptr = unsafe { self.data_ptr.add(self.write_offset as usize) };
        let write_ptr = unsafe { read_head_ptr.add(REC_HEADER_LEN as usize) };
        let available = self.available();
        if available <= REC_HEADER_LEN {
            return Err(WriteError::ChannelFull);
        }
        let len = min(self.header.max_msg_len(), available - REC_HEADER_LEN) as usize;
        let write_res = data.encode_to(&self.df, self.write.reset(write_ptr, len));
        match write_res {
            Ok(0) => Err(WriteError::NoSpaceForRecord),
            Ok(_) => {
                if !self.write.failed {
                    let aligned_rec_len = align(self.write.total as u32 + REC_HEADER_LEN);
                    self.write_metadata(read_head_ptr as *mut u64, self.write.total as u64, aligned_rec_len >> 3);
                    self.write_offset += aligned_rec_len;
                    Ok(aligned_rec_len)
                } else {
                    Err(WriteError::NoSpaceForRecord)
                }
            }
            Err(io_err) => Err(WriteError::EncodingError(io_err)),
        }
    }
    ///Push a heartbeat message into the channel. Hearbeats are zero sized messages which do not need encoding.
    ///Reader should never activate callbacks for heartbeat messsages.
    ///
    /// Returns RecordHeaderLen, 8 in the current version if the operation succeeds.
    ///
    /// # Errors
    ///
    /// If the operation fails a *ChannelFull* error will be returned, which signals that the channel will not accept any new messages.
    ///
    #[allow(clippy::cast_ptr_alignment)]
    #[inline]
    fn heartbeat(&mut self) -> Result<u32, WriteError> {
        let read_head_ptr = unsafe { self.data_ptr.add(self.write_offset as usize) };
        let available = self.available();
        if available <= REC_HEADER_LEN {
            return Err(WriteError::ChannelFull);
        }
        let aligned_rec_len = REC_HEADER_LEN; //no need to align REC_HEADER)LEN must be align
        self.write_metadata(read_head_ptr as *mut u64, 0u64, aligned_rec_len >> 3);
        self.write_offset += aligned_rec_len;
        Ok(aligned_rec_len)
    }

    /// Flushes the channel's outstanding memory map modifications to disk. Calling  this method explicitly
    /// it is not encouraged as flushing does occur automatically and comes with a performance penalty.
    /// It should be used only if for various reasons a writer wants to persist the channel data to the disk
    /// at a higher rate than is done automatically.
    ///
    /// Returns Ok(()) if the operation succeeds.
    ///
    /// # Errors
    ///
    /// If flushing fails an I/O error is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use kekbit_core::tick::TickUnit::Nanos;
    /// use kekbit_core::shm::*;
    /// use kekbit_core::header::Header;
    /// use kekbit_core::api::Writer;
    /// use kekbit_codecs::codecs::raw::RawBinDataFormat;
    ///
    /// const FOREVER: u64 = 99_999_999_999;
    /// let writer_id = 1850;
    /// let channel_id = 42;
    /// let capacity = 30_000;
    /// let max_msg_len = 100;
    /// let header = Header::new(writer_id, channel_id, capacity, max_msg_len, FOREVER, Nanos);
    /// let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
    /// let mut writer = shm_writer(&test_tmp_dir.path(), &header, RawBinDataFormat).unwrap();
    /// let msg = "There are 10 kinds of people: those who know binary and those who don't";
    /// let msg_data = msg.as_bytes();
    /// writer.write(&msg_data).unwrap();
    /// writer.flush().unwrap();
    /// ```
    #[inline]
    fn flush(&mut self) -> Result<(), std::io::Error> {
        debug!("Flushing the channel");
        self.mmap.flush()
    }
}
impl<D: DataFormat> Drop for ShmWriter<D> {
    /// Marks this channel as `closed`, flushes the changes to the disk, and removes the memory mapping.
    fn drop(&mut self) {
        let write_index = self.write_offset;
        info!("Closing message queue..");
        unsafe {
            #[allow(clippy::cast_ptr_alignment)]
            //we should always have the 8 bytes required by CLOSE as they are acounted in the Footer
            let write_ptr = self.data_ptr.offset(write_index as isize) as *mut u64;
            store_atomic_u64(write_ptr, CLOSE, Ordering::Release);
            info!("Closing message sent")
        }
        self.write_offset = self.mmap.len() as u32;
        if self.mmap.flush().is_ok() {
            info!("All changes flushed");
        } else {
            error!("Flush Failed");
        }
    }
}
impl<D: DataFormat> ShmWriter<D> {
    ///Returns the amount of space in this channel still available for write.
    #[inline]
    pub fn available(&self) -> u32 {
        (self.header.capacity() - self.write_offset) & 0xFFFF_FFF8 //rounded down to alignement
    }
    ///Returns the amount of data written into this channel.
    #[inline]
    pub fn write_offset(&self) -> u32 {
        self.write_offset
    }

    ///Returns a reference to the [Header](struct.Header.html) associated with this channel.
    #[inline]
    pub fn header(&self) -> &Header {
        &self.header
    }
    #[inline]
    pub fn data_format(&self) -> &D {
        &self.df
    }
}

struct KekWrite {
    write_ptr: *mut u8,
    max_size: usize,
    total: usize,
    failed: bool,
}

impl KekWrite {
    #[inline]
    fn new(write_ptr: *mut u8, max_size: usize) -> Self {
        KekWrite {
            write_ptr,
            max_size,
            total: 0,
            failed: false,
        }
    }
    #[inline]
    fn reset(&mut self, write_ptr: *mut u8, max_size: usize) -> &mut Self {
        self.write_ptr = write_ptr;
        self.max_size = max_size;
        self.total = 0;
        self.failed = false;
        self
    }
}

impl Write for KekWrite {
    #[inline]
    fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
        if self.failed {
            return Ok(0);
        }
        let data_len = data.len();
        if self.total + data_len > self.max_size {
            self.failed |= true;
            return Ok(0);
        }
        unsafe {
            let crt_ptr = self.write_ptr.add(self.total as usize);
            copy_nonoverlapping(data.as_ptr(), crt_ptr, data_len);
        }
        self.total += data_len;
        Ok(data_len)
    }
    #[inline]
    fn flush(&mut self) -> Result<(), std::io::Error> {
        Ok(())
    }
}

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

    #[test]
    fn test_write() {
        let mut raw_data: [u8; 1000] = [0; 1000];
        let write_ptr = raw_data.as_mut_ptr();
        let mut kw = KekWrite::new(write_ptr, 20);
        kw.flush().unwrap(); //should never crash as it does nothing
        let d1: [u8; 10] = [1; 10];
        let r1 = kw.write(&d1).unwrap();
        assert_eq!(kw.total, r1);
        assert!(!kw.failed);
        for i in 0..10 {
            assert_eq!(raw_data[i], 1);
        }
        kw.flush().unwrap(); //should never crash as it does nothing
        let r2 = kw.write(&d1).unwrap();
        assert_eq!(kw.total, r1 + r2);
        assert!(!kw.failed);
        for i in 10..20 {
            assert_eq!(raw_data[i], 1);
        }
        let r3 = kw.write(&d1).unwrap();
        assert_eq!(0, r3);
        assert!(kw.failed);
        kw.reset(write_ptr, 15);
        assert!(!kw.failed);
        let d2: [u8; 10] = [2; 10];
        let r4 = kw.write(&d2).unwrap();
        assert_eq!(kw.total, r4);
        assert!(!kw.failed);
        for i in 0..10 {
            assert_eq!(raw_data[i], 2);
        }
        assert_eq!(kw.total, 10);
        let r5 = kw.write(&d2).unwrap();
        assert_eq!(0, r5);
        assert!(kw.failed);
        assert_eq!(kw.total, 10);
        //once it fails it will never recover, even if it has enough space
        let r6 = kw.write(&d2[0..3]).unwrap();
        assert_eq!(0, r6);
        assert!(kw.failed);
        assert_eq!(kw.total, 10);
    }
}
<commit_msg>Remove commented attribute.<commit_after>use crate::api::ChannelError::AccessError;
use crate::api::{ChannelError, WriteError, Writer};
use crate::header::Header;
use crate::utils::{align, store_atomic_u64, CLOSE, REC_HEADER_LEN, WATERMARK};
use kekbit_codecs::codecs::DataFormat;
use kekbit_codecs::codecs::Encodable;
use log::{debug, error, info};
use memmap::MmapMut;
use std::cmp::min;
use std::io::Write;
use std::ptr::copy_nonoverlapping;
use std::result::Result;
use std::sync::atomic::Ordering;

/// An implementation of the [Writer](trait.Writer.html) which access a persistent channel through
/// memory mapping, and uses a specific [DataFormat](../codecs/trait.DataFormat.html). A `ShmWriter` must be created using the [shm_writer](fn.shm_writer.html) function.
/// Any `ShmWriter` exclusively holds the channel is bound to, and it is *not thread safe*.
/// If multiple threads must write into a channel they should be externally synchronized.
///
/// # Examples
///
/// ```
/// use kekbit_core::tick::TickUnit::Nanos;
/// use kekbit_core::shm::*;
/// use kekbit_core::header::Header;
/// use kekbit_core::api::Writer;
/// use kekbit_codecs::codecs::raw::RawBinDataFormat;
///
/// const FOREVER: u64 = 99_999_999_999;
/// let writer_id = 1850;
/// let channel_id = 42;
/// let capacity = 3000;
/// let max_msg_len = 100;
/// let header = Header::new(writer_id, channel_id, capacity, max_msg_len, FOREVER, Nanos);
/// let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
/// let mut writer = shm_writer(&test_tmp_dir.path(), &header, RawBinDataFormat).unwrap();
/// writer.heartbeat().unwrap();
/// ```
pub struct ShmWriter<D: DataFormat> {
    header: Header,
    data_ptr: *mut u8,
    write_offset: u32,
    mmap: MmapMut,
    df: D,
    write: KekWrite,
}

impl<D: DataFormat> ShmWriter<D> {
    #[allow(clippy::cast_ptr_alignment)]
    pub(super) fn new(mut mmap: MmapMut, df: D) -> Result<ShmWriter<D>, ChannelError> {
        let buf = &mut mmap[..];
        let header = Header::read(buf)?;
        let header_ptr = buf.as_ptr() as *mut u64;
        let head_len = header.len();
        let data_ptr = unsafe { header_ptr.add(head_len) } as *mut u8;
        let write = KekWrite::new(data_ptr, header.max_msg_len() as usize);
        let mut writer = ShmWriter {
            header,
            data_ptr,
            write_offset: 0,
            mmap,
            df,
            write,
        };
        info!(
            "Kekbit channel writer created. Size is {}MB. Max msg size {}KB",
            writer.header.capacity() / 1_000_000,
            writer.header.max_msg_len() / 1_000
        );
        //sent the very first original heart bear
        match writer.heartbeat() {
            Ok(_) => {
                info!("Initial hearbeat successfully sent!");
                Ok(writer)
            }
            Err(we) => Err(AccessError {
                reason: format!("Initial heartbeat failed!. Reason {:?}", we),
            }),
        }
    }

    #[inline(always)]
    fn write_metadata(&mut self, write_ptr: *mut u64, len: u64, aligned_rec_len: u32) {
        unsafe {
            //we should always have the 8 bytes required by WATERMARK as they are acounted in the Footer
            store_atomic_u64(write_ptr.add(aligned_rec_len as usize), WATERMARK, Ordering::Release);
        }
        store_atomic_u64(write_ptr, len, Ordering::Release);
    }
}

impl<D: DataFormat> Writer<D> for ShmWriter<D> {
    /// Writes a message into the channel. This operation will encode the data directly into  channel.
    /// While this is a non blocking operation, only one write should be executed at any given time.
    ///
    /// Returns the total amount of bytes wrote into the channel which includes, the size of the message,
    /// the size of the message header and the amount of padding add to that message.
    ///
    /// # Arguments
    ///
    /// * `data` - The  data which to encode and  write into the channel.
    ///
    /// # Errors
    ///
    /// Two kinds of [failures](enum.WriteError.html) may occur. One if the encoding operation failed, the other if the channel
    /// rejected the message for reasons such data is too large or no space is available in the channel.
    ///
    /// # Examples
    ///
    /// ```
    /// use kekbit_core::tick::TickUnit::Nanos;
    /// use kekbit_core::shm::*;
    /// use kekbit_core::header::Header;
    /// use kekbit_core::api::Writer;
    /// use kekbit_codecs::codecs::raw::RawBinDataFormat;
    ///
    /// const FOREVER: u64 = 99_999_999_999;
    /// let writer_id = 1850;
    /// let channel_id = 42;
    /// let capacity = 30_000;
    /// let max_msg_len = 100;
    /// let header = Header::new(writer_id, channel_id, capacity, max_msg_len, FOREVER, Nanos);
    /// let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
    /// let mut writer = shm_writer(&test_tmp_dir.path(), &header, RawBinDataFormat).unwrap();
    /// let msg = "There are 10 kinds of people: those who know binary and those who don't";
    /// let msg_data = msg.as_bytes();
    /// writer.write(&msg_data).unwrap();
    /// ```
    ///
    #[allow(clippy::cast_ptr_alignment)]
    fn write(&mut self, data: &impl Encodable<D>) -> Result<u32, WriteError> {
        let read_head_ptr = unsafe { self.data_ptr.add(self.write_offset as usize) };
        let write_ptr = unsafe { read_head_ptr.add(REC_HEADER_LEN as usize) };
        let available = self.available();
        if available <= REC_HEADER_LEN {
            return Err(WriteError::ChannelFull);
        }
        let len = min(self.header.max_msg_len(), available - REC_HEADER_LEN) as usize;
        let write_res = data.encode_to(&self.df, self.write.reset(write_ptr, len));
        match write_res {
            Ok(0) => Err(WriteError::NoSpaceForRecord),
            Ok(_) => {
                if !self.write.failed {
                    let aligned_rec_len = align(self.write.total as u32 + REC_HEADER_LEN);
                    self.write_metadata(read_head_ptr as *mut u64, self.write.total as u64, aligned_rec_len >> 3);
                    self.write_offset += aligned_rec_len;
                    Ok(aligned_rec_len)
                } else {
                    Err(WriteError::NoSpaceForRecord)
                }
            }
            Err(io_err) => Err(WriteError::EncodingError(io_err)),
        }
    }
    ///Push a heartbeat message into the channel. Hearbeats are zero sized messages which do not need encoding.
    ///Reader should never activate callbacks for heartbeat messsages.
    ///
    /// Returns RecordHeaderLen, 8 in the current version if the operation succeeds.
    ///
    /// # Errors
    ///
    /// If the operation fails a *ChannelFull* error will be returned, which signals that the channel will not accept any new messages.
    ///
    #[allow(clippy::cast_ptr_alignment)]
    #[inline]
    fn heartbeat(&mut self) -> Result<u32, WriteError> {
        let read_head_ptr = unsafe { self.data_ptr.add(self.write_offset as usize) };
        let available = self.available();
        if available <= REC_HEADER_LEN {
            return Err(WriteError::ChannelFull);
        }
        let aligned_rec_len = REC_HEADER_LEN; //no need to align REC_HEADER)LEN must be align
        self.write_metadata(read_head_ptr as *mut u64, 0u64, aligned_rec_len >> 3);
        self.write_offset += aligned_rec_len;
        Ok(aligned_rec_len)
    }

    /// Flushes the channel's outstanding memory map modifications to disk. Calling  this method explicitly
    /// it is not encouraged as flushing does occur automatically and comes with a performance penalty.
    /// It should be used only if for various reasons a writer wants to persist the channel data to the disk
    /// at a higher rate than is done automatically.
    ///
    /// Returns Ok(()) if the operation succeeds.
    ///
    /// # Errors
    ///
    /// If flushing fails an I/O error is returned.
    ///
    /// # Examples
    ///
    /// ```
    /// use kekbit_core::tick::TickUnit::Nanos;
    /// use kekbit_core::shm::*;
    /// use kekbit_core::header::Header;
    /// use kekbit_core::api::Writer;
    /// use kekbit_codecs::codecs::raw::RawBinDataFormat;
    ///
    /// const FOREVER: u64 = 99_999_999_999;
    /// let writer_id = 1850;
    /// let channel_id = 42;
    /// let capacity = 30_000;
    /// let max_msg_len = 100;
    /// let header = Header::new(writer_id, channel_id, capacity, max_msg_len, FOREVER, Nanos);
    /// let test_tmp_dir = tempdir::TempDir::new("kektest").unwrap();
    /// let mut writer = shm_writer(&test_tmp_dir.path(), &header, RawBinDataFormat).unwrap();
    /// let msg = "There are 10 kinds of people: those who know binary and those who don't";
    /// let msg_data = msg.as_bytes();
    /// writer.write(&msg_data).unwrap();
    /// writer.flush().unwrap();
    /// ```
    #[inline]
    fn flush(&mut self) -> Result<(), std::io::Error> {
        debug!("Flushing the channel");
        self.mmap.flush()
    }
}
impl<D: DataFormat> Drop for ShmWriter<D> {
    /// Marks this channel as `closed`, flushes the changes to the disk, and removes the memory mapping.
    fn drop(&mut self) {
        let write_index = self.write_offset;
        info!("Closing message queue..");
        unsafe {
            #[allow(clippy::cast_ptr_alignment)]
            //we should always have the 8 bytes required by CLOSE as they are acounted in the Footer
            let write_ptr = self.data_ptr.offset(write_index as isize) as *mut u64;
            store_atomic_u64(write_ptr, CLOSE, Ordering::Release);
            info!("Closing message sent")
        }
        self.write_offset = self.mmap.len() as u32;
        if self.mmap.flush().is_ok() {
            info!("All changes flushed");
        } else {
            error!("Flush Failed");
        }
    }
}
impl<D: DataFormat> ShmWriter<D> {
    ///Returns the amount of space in this channel still available for write.
    #[inline]
    pub fn available(&self) -> u32 {
        (self.header.capacity() - self.write_offset) & 0xFFFF_FFF8 //rounded down to alignement
    }
    ///Returns the amount of data written into this channel.
    #[inline]
    pub fn write_offset(&self) -> u32 {
        self.write_offset
    }

    ///Returns a reference to the [Header](struct.Header.html) associated with this channel.
    #[inline]
    pub fn header(&self) -> &Header {
        &self.header
    }
    #[inline]
    pub fn data_format(&self) -> &D {
        &self.df
    }
}

struct KekWrite {
    write_ptr: *mut u8,
    max_size: usize,
    total: usize,
    failed: bool,
}

impl KekWrite {
    #[inline]
    fn new(write_ptr: *mut u8, max_size: usize) -> Self {
        KekWrite {
            write_ptr,
            max_size,
            total: 0,
            failed: false,
        }
    }
    #[inline]
    fn reset(&mut self, write_ptr: *mut u8, max_size: usize) -> &mut Self {
        self.write_ptr = write_ptr;
        self.max_size = max_size;
        self.total = 0;
        self.failed = false;
        self
    }
}

impl Write for KekWrite {
    #[inline]
    fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
        if self.failed {
            return Ok(0);
        }
        let data_len = data.len();
        if self.total + data_len > self.max_size {
            self.failed |= true;
            return Ok(0);
        }
        unsafe {
            let crt_ptr = self.write_ptr.add(self.total as usize);
            copy_nonoverlapping(data.as_ptr(), crt_ptr, data_len);
        }
        self.total += data_len;
        Ok(data_len)
    }
    #[inline]
    fn flush(&mut self) -> Result<(), std::io::Error> {
        Ok(())
    }
}

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

    #[test]
    fn test_write() {
        let mut raw_data: [u8; 1000] = [0; 1000];
        let write_ptr = raw_data.as_mut_ptr();
        let mut kw = KekWrite::new(write_ptr, 20);
        kw.flush().unwrap(); //should never crash as it does nothing
        let d1: [u8; 10] = [1; 10];
        let r1 = kw.write(&d1).unwrap();
        assert_eq!(kw.total, r1);
        assert!(!kw.failed);
        for i in 0..10 {
            assert_eq!(raw_data[i], 1);
        }
        kw.flush().unwrap(); //should never crash as it does nothing
        let r2 = kw.write(&d1).unwrap();
        assert_eq!(kw.total, r1 + r2);
        assert!(!kw.failed);
        for i in 10..20 {
            assert_eq!(raw_data[i], 1);
        }
        let r3 = kw.write(&d1).unwrap();
        assert_eq!(0, r3);
        assert!(kw.failed);
        kw.reset(write_ptr, 15);
        assert!(!kw.failed);
        let d2: [u8; 10] = [2; 10];
        let r4 = kw.write(&d2).unwrap();
        assert_eq!(kw.total, r4);
        assert!(!kw.failed);
        for i in 0..10 {
            assert_eq!(raw_data[i], 2);
        }
        assert_eq!(kw.total, 10);
        let r5 = kw.write(&d2).unwrap();
        assert_eq!(0, r5);
        assert!(kw.failed);
        assert_eq!(kw.total, 10);
        //once it fails it will never recover, even if it has enough space
        let r6 = kw.write(&d2[0..3]).unwrap();
        assert_eq!(0, r6);
        assert!(kw.failed);
        assert_eq!(kw.total, 10);
    }
}
<|endoftext|>


<commit_before>// Claxon -- A FLAC decoding library in Rust
// Copyright 2014 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

extern crate claxon;
extern crate hound;

use std::fs;
use std::io;
use std::path::Path;

fn run_metaflac<P: AsRef<Path>>(fname: P) -> String {
    use std::process::Command;

    // Run metaflac on the specified file and print all streaminfo data.
    let output = Command::new("metaflac")
                     .arg("--show-min-blocksize")
                     .arg("--show-max-blocksize")
                     .arg("--show-min-framesize")
                     .arg("--show-max-framesize")
                     .arg("--show-sample-rate")
                     .arg("--show-channels")
                     .arg("--show-bps")
                     .arg("--show-total-samples")
                     .arg("--show-md5sum")
                     .arg(fname.as_ref().to_str().expect("unsupported filename"))
                     .output()
                     .expect("failed to run metaflac");
    String::from_utf8(output.stdout).expect("metaflac wrote invalid UTF-8")
}

fn decode_file<P: AsRef<Path>>(fname: P) {
    use std::process::Command;

    // Run the the reference flac decoder on the file.
    let success = Command::new("flac")
                      .arg("--decode")
                      .arg("--silent")
                      .arg(fname.as_ref().to_str().expect("unsupported filename"))
                      .status()
                      .expect("failed to run flac")
                      .success();
    assert!(success);
}

fn print_hex(seq: &[u8]) -> String {
    let vec: Vec<String> = seq.iter().map(|x| format!("{:0>2x}", *x)).collect();
    vec.concat()
}

fn read_streaminfo<P: AsRef<Path>>(fname: P) -> String {
    // Use a buffered reader, this speeds up the test by 120%.
    let file = fs::File::open(fname).unwrap();
    let reader = io::BufReader::new(file);
    let stream = claxon::FlacReader::new(reader).unwrap();
    let streaminfo = stream.streaminfo();

    // Format the streaminfo in the same way that metaflac prints it.
    format!("{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
            streaminfo.min_block_size,
            streaminfo.max_block_size,
            streaminfo.min_frame_size.unwrap_or(0),
            streaminfo.max_frame_size.unwrap_or(0),
            streaminfo.sample_rate,
            streaminfo.channels,
            streaminfo.bits_per_sample,
            streaminfo.samples.unwrap_or(0),
            print_hex(&streaminfo.md5sum)) // TODO implement LowerHex for &[u8] and submit a PR.
}

fn compare_metaflac<P: AsRef<Path>>(fname: P) {
    let metaflac = run_metaflac(&fname);
    let streaminfo = read_streaminfo(&fname);
    let mut mf_lines = metaflac.lines();
    let mut si_lines = streaminfo.lines();
    while let (Some(mf), Some(si)) = (mf_lines.next(), si_lines.next()) {
        if mf != si {
            println!("metaflac\n--------\n{}", metaflac);
            println!("streaminfo\n----------\n{}", streaminfo);
            panic!("metaflac disagrees on parsed streaminfo");
        }
    }
}

fn compare_decoded_stream<P: AsRef<Path>>(fname: P) {
    let ref_fname = fname.as_ref().with_extension("wav");
    if !ref_fname.exists() {
        // TODO: actually, we might only want to run the test for files that
        // do exist already. It is fine to run streaminfo or decode-only tests
        // on thousands of files, but decoding thousands of files to wav before
        // running the tests will take a lot of space. For now, while the
        // number of samples is closer to 10 than to 100, this is fine.
        decode_file(&fname);
    }

    // If the reference file does exist after decoding, we can compare it to
    // how Claxon decodes it, sample by sample.
    if ref_fname.exists() {
        let mut ref_wav_reader = hound::WavReader::open(ref_fname).unwrap();

        let try_file = fs::File::open(fname).unwrap();
        let try_buf_reader = io::BufReader::new(try_file);
        let mut try_flac_reader = claxon::FlacReader::new(try_buf_reader).unwrap();

        // The streaminfo test will ensure that things like bit depth and
        // sample rate match, only the actual samples are compared here.

        let mut ref_samples = ref_wav_reader.samples::<i32>();

        let samples = try_flac_reader.streaminfo().samples.unwrap();
        let n_channels = try_flac_reader.streaminfo().channels;
        let mut blocks = try_flac_reader.blocks();
        let mut sample = 0u64;
        let mut b = 0u64;
        let mut buffer = Vec::new();

        while sample < samples {
            let block = blocks.read_next_or_eof(buffer).unwrap().unwrap();
            {
                let mut channels: Vec<_> = (0..n_channels)
                                               .map(|i| block.channel(i).iter().cloned())
                                               .collect();
                for i in 0..block.duration() {
                    for ch in 0..n_channels as usize {
                        let ref_sample = ref_samples.next().map(|r| r.ok().unwrap());
                        let try_sample = channels[ch].next();
                        if ref_sample != try_sample {
                            println!("disagreement at sample {} of block {} in channel {}: \
                                      reference is {} but decoded is {}",
                                     i,
                                     b,
                                     ch,
                                     ref_sample.unwrap(),
                                     try_sample.unwrap());
                            panic!("decoding differs from reference decoder");
                        }
                    }
                }
                sample = sample + block.duration() as u64;
            }
            b = b + 1;
            buffer = block.into_buffer();
        }
    }
}

// Hard-coded tests for the test samples added py populate.sh.
// This allows us to run these tests in parallel and follow progress without
// enabling --nocapture.

#[test]
fn verify_streaminfo_p0() {
    compare_metaflac("testsamples/p0.flac");
}

#[test]
fn verify_streaminfo_p1() {
    compare_metaflac("testsamples/p1.flac");
}

#[test]
fn verify_streaminfo_p2() {
    compare_metaflac("testsamples/p2.flac");
}

#[test]
fn verify_streaminfo_p3() {
    compare_metaflac("testsamples/p3.flac");
}

#[test]
fn verify_streaminfo_p4() {
    compare_metaflac("testsamples/p4.flac");
}

#[test]
fn verify_streaminfo_pop() {
    compare_metaflac("testsamples/pop.flac");
}

#[test]
fn verify_streaminfo_short() {
    compare_metaflac("testsamples/short.flac");
}

#[test]
fn verify_decoded_stream_p0() {
    compare_decoded_stream("testsamples/p0.flac");
}

#[test]
fn verify_decoded_stream_p1() {
    compare_decoded_stream("testsamples/p1.flac");
}

#[test]
fn verify_decoded_stream_p2() {
    compare_decoded_stream("testsamples/p2.flac");
}

#[test]
fn verify_decoded_stream_p3() {
    compare_decoded_stream("testsamples/p3.flac");
}

#[test]
fn verify_decoded_stream_p4() {
    compare_decoded_stream("testsamples/p4.flac");
}

#[test]
fn verify_decoded_stream_pop() {
    compare_decoded_stream("testsamples/pop.flac");
}

#[test]
fn verify_decoded_stream_short() {
    // The short sample has only 4 samples, even less than pop.flac.
    compare_decoded_stream("testsamples/short.flac");
}

#[test]
fn verify_extra_samples() {
    use std::ffi::OsStr;

    if !Path::new("testsamples/extra").exists() {
        return
    }

    // Enumerate all the flac files in the testsamples/extra directory, and
    // compare the streaminfo and stream itself for those.
    let dir = fs::read_dir("testsamples/extra")
                 .ok().expect("failed to enumerate flac files");
    for path in dir {
        let path = path.ok().expect("failed to obtain path info").path();
        if path.is_file() && path.extension() == Some(OsStr::new("flac")) {
            print!("    comparing {} ...", path.to_str()
                                               .expect("unsupported filename"));
            compare_metaflac(&path);
            compare_decoded_stream(&path);
            println!(" ok");
        }
    }
}

#[test]
fn regression_test_fuzz_samples() {
    use std::ffi::OsStr;

    // Enumerate all the flac files in the testsamples/fuzz directory,
    // and ensure that they can be decoded without panic.
    let dir = fs::read_dir("testsamples/fuzz")
                 .ok().expect("failed to enumerate flac files");
    for path in dir {
        let path = path.ok().expect("failed to obtain path info").path();
        if path.is_file() && path.extension() == Some(OsStr::new("flac")) {
            print!("    regression testing {} ...", path.to_str()
                   .expect("unsupported filename"));

            if let Ok(mut reader) = claxon::FlacReader::open(&path) {
                let mut buffer = Vec::new();
                while let Ok(Some(block)) = reader.blocks().read_next_or_eof(buffer) {
                    buffer = block.into_buffer();
                }
            }
            println!(" ok");
        }
    }
}
<commit_msg>Verify against in-memory files, do not store .wav<commit_after>// Claxon -- A FLAC decoding library in Rust
// Copyright 2014 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

extern crate claxon;
extern crate hound;

use std::fs;
use std::io;
use std::path::Path;

fn run_metaflac<P: AsRef<Path>>(fname: P) -> String {
    use std::process::Command;

    // Run metaflac on the specified file and print all streaminfo data.
    let output = Command::new("metaflac")
        .arg("--show-min-blocksize")
        .arg("--show-max-blocksize")
        .arg("--show-min-framesize")
        .arg("--show-max-framesize")
        .arg("--show-sample-rate")
        .arg("--show-channels")
        .arg("--show-bps")
        .arg("--show-total-samples")
        .arg("--show-md5sum")
        .arg(fname.as_ref().to_str().expect("unsupported filename"))
        .output()
        .expect("failed to run metaflac");
    String::from_utf8(output.stdout).expect("metaflac wrote invalid UTF-8")
}

fn decode_file<P: AsRef<Path>>(fname: P) -> Vec<u8> {
    use std::process::Command;

    // Run the the reference flac decoder on the file.
    let output = Command::new("flac")
        .arg("--decode")
        .arg("--silent")
        .arg("--stdout")
        .arg(fname.as_ref().to_str().expect("unsupported filename"))
        .output()
        .expect("failed to run flac");

    assert!(output.status.success());
    output.stdout
}

fn print_hex(seq: &[u8]) -> String {
    let vec: Vec<String> = seq.iter().map(|x| format!("{:0>2x}", *x)).collect();
    vec.concat()
}

fn read_streaminfo<P: AsRef<Path>>(fname: P) -> String {
    // Use a buffered reader, this speeds up the test by 120%.
    let file = fs::File::open(fname).unwrap();
    let reader = io::BufReader::new(file);
    let stream = claxon::FlacReader::new(reader).unwrap();
    let streaminfo = stream.streaminfo();

    // Format the streaminfo in the same way that metaflac prints it.
    format!("{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
            streaminfo.min_block_size,
            streaminfo.max_block_size,
            streaminfo.min_frame_size.unwrap_or(0),
            streaminfo.max_frame_size.unwrap_or(0),
            streaminfo.sample_rate,
            streaminfo.channels,
            streaminfo.bits_per_sample,
            streaminfo.samples.unwrap_or(0),
            print_hex(&streaminfo.md5sum)) // TODO implement LowerHex for &[u8] and submit a PR.
}

fn compare_metaflac<P: AsRef<Path>>(fname: P) {
    let metaflac = run_metaflac(&fname);
    let streaminfo = read_streaminfo(&fname);
    let mut mf_lines = metaflac.lines();
    let mut si_lines = streaminfo.lines();
    while let (Some(mf), Some(si)) = (mf_lines.next(), si_lines.next()) {
        if mf != si {
            println!("metaflac\n--------\n{}", metaflac);
            println!("streaminfo\n----------\n{}", streaminfo);
            panic!("metaflac disagrees on parsed streaminfo");
        }
    }
}

fn compare_decoded_stream<P: AsRef<Path>>(fname: P) {
    let wav = decode_file(&fname);
    let cursor = io::Cursor::new(wav);

    // Compare the reference decoded by the 'flac' program (stored as wav, which
    // we read with Hound) to how Claxon decodes it, sample by sample.
    let mut ref_wav_reader = hound::WavReader::new(cursor).unwrap();

    let try_file = fs::File::open(fname).unwrap();
    let try_buf_reader = io::BufReader::new(try_file);
    let mut try_flac_reader = claxon::FlacReader::new(try_buf_reader).unwrap();

    // The streaminfo test will ensure that things like bit depth and
    // sample rate match, only the actual samples are compared here.
    let mut ref_samples = ref_wav_reader.samples::<i32>();

    let samples = try_flac_reader.streaminfo().samples.unwrap();
    let n_channels = try_flac_reader.streaminfo().channels;
    let mut blocks = try_flac_reader.blocks();
    let mut sample = 0u64;
    let mut b = 0u64;
    let mut buffer = Vec::new();

    while sample < samples {
        let block = blocks.read_next_or_eof(buffer).unwrap().unwrap();
        {
            let mut channels: Vec<_> = (0..n_channels)
                                           .map(|i| block.channel(i).iter().cloned())
                                           .collect();
            for i in 0..block.duration() {
                for ch in 0..n_channels as usize {
                    let ref_sample = ref_samples.next().map(|r| r.ok().unwrap());
                    let try_sample = channels[ch].next();
                    if ref_sample != try_sample {
                        println!("disagreement at sample {} of block {} in channel {}: \
                                  reference is {} but decoded is {}",
                                 i,
                                 b,
                                 ch,
                                 ref_sample.unwrap(),
                                 try_sample.unwrap());
                        panic!("decoding differs from reference decoder");
                    }
                }
            }
            sample = sample + block.duration() as u64;
        }
        b = b + 1;
        buffer = block.into_buffer();
    }
}

// Hard-coded tests for the test samples added py populate.sh.
// This allows us to run these tests in parallel and follow progress without
// enabling --nocapture.

#[test]
fn verify_streaminfo_p0() {
    compare_metaflac("testsamples/p0.flac");
}

#[test]
fn verify_streaminfo_p1() {
    compare_metaflac("testsamples/p1.flac");
}

#[test]
fn verify_streaminfo_p2() {
    compare_metaflac("testsamples/p2.flac");
}

#[test]
fn verify_streaminfo_p3() {
    compare_metaflac("testsamples/p3.flac");
}

#[test]
fn verify_streaminfo_p4() {
    compare_metaflac("testsamples/p4.flac");
}

#[test]
fn verify_streaminfo_pop() {
    compare_metaflac("testsamples/pop.flac");
}

#[test]
fn verify_streaminfo_short() {
    compare_metaflac("testsamples/short.flac");
}

#[test]
fn verify_decoded_stream_p0() {
    compare_decoded_stream("testsamples/p0.flac");
}

#[test]
fn verify_decoded_stream_p1() {
    compare_decoded_stream("testsamples/p1.flac");
}

#[test]
fn verify_decoded_stream_p2() {
    compare_decoded_stream("testsamples/p2.flac");
}

#[test]
fn verify_decoded_stream_p3() {
    compare_decoded_stream("testsamples/p3.flac");
}

#[test]
fn verify_decoded_stream_p4() {
    compare_decoded_stream("testsamples/p4.flac");
}

#[test]
fn verify_decoded_stream_pop() {
    compare_decoded_stream("testsamples/pop.flac");
}

#[test]
fn verify_decoded_stream_short() {
    // The short sample has only 4 samples, even less than pop.flac.
    compare_decoded_stream("testsamples/short.flac");
}

#[test]
fn verify_extra_samples() {
    use std::ffi::OsStr;

    if !Path::new("testsamples/extra").exists() {
        return
    }

    // Enumerate all the flac files in the testsamples/extra directory, and
    // compare the streaminfo and stream itself for those.
    let dir = fs::read_dir("testsamples/extra")
                 .ok().expect("failed to enumerate flac files");
    for path in dir {
        let path = path.ok().expect("failed to obtain path info").path();
        if path.is_file() && path.extension() == Some(OsStr::new("flac")) {
            print!("    comparing {} ...", path.to_str()
                                               .expect("unsupported filename"));
            compare_metaflac(&path);
            compare_decoded_stream(&path);
            println!(" ok");
        }
    }
}

#[test]
fn regression_test_fuzz_samples() {
    use std::ffi::OsStr;

    // Enumerate all the flac files in the testsamples/fuzz directory,
    // and ensure that they can be decoded without panic.
    let dir = fs::read_dir("testsamples/fuzz")
                 .ok().expect("failed to enumerate flac files");
    for path in dir {
        let path = path.ok().expect("failed to obtain path info").path();
        if path.is_file() && path.extension() == Some(OsStr::new("flac")) {
            print!("    regression testing {} ...", path.to_str()
                   .expect("unsupported filename"));

            if let Ok(mut reader) = claxon::FlacReader::open(&path) {
                let mut buffer = Vec::new();
                while let Ok(Some(block)) = reader.blocks().read_next_or_eof(buffer) {
                    buffer = block.into_buffer();
                }
            }
            println!(" ok");
        }
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate lancelot;
extern crate log;
#[macro_use]
extern crate clap;

use better_panic;
use failure::Error;
use fern;
use log::{debug, error, info};

use lancelot::workspace::Workspace;

fn handle_functions(ws: &Workspace) -> Result<(), Error> {
    let mut functions = ws.get_functions().collect::<Vec<_>>();
    functions.sort();

    info!("found {} functions", functions.len());
    for rva in functions.iter() {
        if let Ok(basic_blocks) = ws.get_basic_blocks(**rva) {
            println!("{} with {} basic blocks", ws.va(**rva).unwrap(), basic_blocks.len());
        } else {
            println!("{}", rva);
        }
    }

    Ok(())
}

fn main() {
    better_panic::install();

    let matches = clap_app!(lancelot =>
        (author: "Willi Ballenthin <willi.ballenthin@gmail.com>")
        (about: "Binary analysis framework")
        (@arg verbose: -v --verbose +multiple "log verbose messages")
        (@arg quiet: -q --quiet "disable informational messages")
        (@subcommand functions =>
            (about: "find functions")
            (@arg input: +required "path to file to analyze"))
        (@subcommand smoketest =>
            (about: "analyze file and exit on analysis failure")
            (@arg input: +required "path to file to analyze"))
    )
    .get_matches();

    // --quiet overrides --verbose
    let log_level = if matches.is_present("quiet") {
        log::LevelFilter::Error
    } else {
        match matches.occurrences_of("verbose") {
            0 => log::LevelFilter::Info,
            1 => log::LevelFilter::Debug,
            2 => log::LevelFilter::Trace,
            _ => log::LevelFilter::Trace,
        }
    };

    fern::Dispatch::new()
        .format(move |out, message, record| {
            out.finish(format_args!(
                "{} [{:5}] {} {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                if log_level == log::LevelFilter::Trace {
                    record.target()
                } else {
                    ""
                },
                message
            ))
        })
        .level(log_level)
        .chain(std::io::stderr())
        .filter(|metadata| !metadata.target().starts_with("goblin::pe"))
        .apply()
        .expect("failed to configure logging");

    if let Some(matches) = matches.subcommand_matches("functions") {
        debug!("mode: find functions");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        let ws = Workspace::from_file(filename).unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        let ws = ws.load().unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        if let Err(e) = handle_functions(&ws) {
            error!("error: {}", e)
        }
    } else if let Some(matches) = matches.subcommand_matches("smoketest") {
        debug!("mode: smoketest");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        match Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e))
            .enable_strict_mode()
            .load()
        {
            Err(e) => {
                println!("error");
                eprintln!("{:?}", e);
            }
            Ok(_) => println!("ok"),
        }
    };
}
<commit_msg>bin/lancelot: use config<commit_after>extern crate chrono;
extern crate lancelot;
extern crate log;
#[macro_use]
extern crate clap;

use std::path::PathBuf;

use better_panic;
use failure::Error;
use fern;
use log::{debug, error, info};

use lancelot::{config::Config, workspace::Workspace};

fn handle_functions(ws: &Workspace) -> Result<(), Error> {
    let mut functions = ws.get_functions().collect::<Vec<_>>();
    functions.sort();

    info!("found {} functions", functions.len());
    for rva in functions.iter() {
        if let Ok(basic_blocks) = ws.get_basic_blocks(**rva) {
            println!("{} with {} basic blocks", ws.va(**rva).unwrap(), basic_blocks.len());
        } else {
            println!("{}", rva);
        }
    }

    Ok(())
}

fn main() {
    better_panic::install();

    let mut config: Config = Default::default();

    let matches = clap_app!(lancelot =>
        (author: "Willi Ballenthin <willi.ballenthin@gmail.com>")
        (about: "Binary analysis framework")
        (@arg verbose: -v --verbose +multiple "log verbose messages")
        (@arg quiet: -q --quiet "disable informational messages")
        (@subcommand functions =>
            (about: "find functions")
            (@arg input: +required "path to file to analyze"))
        (@subcommand smoketest =>
            (about: "analyze file and exit on analysis failure")
            (@arg input: +required "path to file to analyze"))
    )
    .get_matches();

    // --quiet overrides --verbose
    let log_level = if matches.is_present("quiet") {
        log::LevelFilter::Error
    } else {
        match matches.occurrences_of("verbose") {
            0 => log::LevelFilter::Info,
            1 => log::LevelFilter::Debug,
            2 => log::LevelFilter::Trace,
            _ => log::LevelFilter::Trace,
        }
    };

    fern::Dispatch::new()
        .format(move |out, message, record| {
            out.finish(format_args!(
                "{} [{:5}] {} {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                record.level(),
                if log_level == log::LevelFilter::Trace {
                    record.target()
                } else {
                    ""
                },
                message
            ))
        })
        .level(log_level)
        .chain(std::io::stderr())
        .filter(|metadata| !metadata.target().starts_with("goblin::pe"))
        .apply()
        .expect("failed to configure logging");

    let pat_dir = config.analysis.flirt.pat_dir.to_str().unwrap().to_string();
    let sig_dir = config.analysis.flirt.sig_dir.to_str().unwrap().to_string();
    config.analysis.flirt.pat_dir = PathBuf::from(shellexpand::tilde(&pat_dir).into_owned());
    config.analysis.flirt.sig_dir = PathBuf::from(shellexpand::tilde(&sig_dir).into_owned());
    debug!("config:\n{:#?}", config);

    if let Some(matches) = matches.subcommand_matches("functions") {
        debug!("mode: find functions");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        let ws = Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e))
            .with_config(config);

        let ws = ws.load().unwrap_or_else(|e| panic!("failed to load workspace: {}", e));

        if let Err(e) = handle_functions(&ws) {
            error!("error: {}", e)
        }
    } else if let Some(matches) = matches.subcommand_matches("smoketest") {
        debug!("mode: smoketest");

        let filename = matches.value_of("input").unwrap();
        debug!("input: {}", filename);

        match Workspace::from_file(filename)
            .unwrap_or_else(|e| panic!("failed to load workspace: {}", e))
            .with_config(config)
            .enable_strict_mode()
            .load()
        {
            Err(e) => {
                println!("error");
                eprintln!("{:?}", e);
            }
            Ok(_) => println!("ok"),
        }
    };
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Daniel Grunwald
//
// 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.

extern crate num;

use libc::{c_long, c_double};
use std;
use python::{Python, PythonObject, ToPythonPointer};
use err::{self, PyResult, PyErr};
use super::object::PyObject;
use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};

#[cfg(feature="python27-sys")]
pyobject_newtype!(PyInt, PyInt_Check, PyInt_Type);

pyobject_newtype!(PyLong, PyLong_Check, PyLong_Type);
pyobject_newtype!(PyFloat, PyFloat_Check, PyFloat_Type);

#[cfg(feature="python27-sys")]
impl <'p> PyInt<'p> {
    /// Creates a new python `int` object.
    pub fn new(py: Python<'p>, val: c_long) -> PyInt<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyInt_FromLong(val))
        }
    }

    /// Gets the value of this integer.
    pub fn value(&self) -> c_long {
        unsafe { ffi::PyInt_AS_LONG(self.as_ptr()) }
    }
}

impl <'p> PyFloat<'p> {
    /// Creates a new python `float` object.
    pub fn new(py: Python<'p>, val: c_double) -> PyFloat<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyFloat_FromDouble(val))
        }
    }

    /// Gets the value of this float.
    pub fn value(&self) -> c_double {
        unsafe { ffi::PyFloat_AsDouble(self.as_ptr()) }
    }
}

macro_rules! int_fits_c_long(
    ($rust_type:ty) => (
        #[cfg(feature="python27-sys")]
        impl <'p> ToPyObject<'p> for $rust_type {
            type ObjectType = PyInt<'p>;

            fn to_py_object(&self, py: Python<'p>) -> PyInt<'p> {
                unsafe {
                    err::cast_from_owned_ptr_or_panic(py,
                        ffi::PyInt_FromLong(*self as c_long))
                }
            }
        }

        #[cfg(feature="python3-sys")]
        impl <'p> ToPyObject<'p> for $rust_type {
            type ObjectType = PyLong<'p>;

            fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
                unsafe {
                    err::cast_from_owned_ptr_or_panic(py,
                        ffi::PyLong_FromLong(*self as c_long))
                }
            }
        }

        #[cfg(feature="python27-sys")]
        impl <'p> FromPyObject<'p> for $rust_type {
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let val = unsafe { ffi::PyInt_AsLong(s.as_ptr()) };
                if val == -1 && PyErr::occurred(py) {
                    return Err(PyErr::fetch(py));
                }
                match num::traits::cast::<c_long, $rust_type>(val) {
                    Some(v) => Ok(v),
                    None => Err(overflow_error(py))
                }
            }
        }

        #[cfg(feature="python3-sys")]
        impl <'p> FromPyObject<'p> for $rust_type {
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let val = unsafe { ffi::PyLong_AsLong(s.as_ptr()) };
                if val == -1 && PyErr::occurred(py) {
                    return Err(PyErr::fetch(py));
                }
                match num::traits::cast::<c_long, $rust_type>(val) {
                    Some(v) => Ok(v),
                    None => Err(overflow_error(py))
                }
            }
        }
    )
);

macro_rules! int_fits_larger_int(
    ($rust_type:ty, $larger_type:ty) => (
        impl <'p> ToPyObject<'p> for $rust_type {
            type ObjectType = <$larger_type as ToPyObject<'p>>::ObjectType;

            #[inline]
            fn to_py_object(&self, py: Python<'p>) -> <$larger_type as ToPyObject<'p>>::ObjectType {
                (*self as $larger_type).to_py_object(py)
            }
        }

        impl <'p> FromPyObject<'p> for $rust_type {
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let val = try!(s.extract::<$larger_type>());
                match num::traits::cast::<$larger_type, $rust_type>(val) {
                    Some(v) => Ok(v),
                    None => Err(overflow_error(py))
                }
            }
        }
    )
);

int_fits_c_long!(i8);
int_fits_c_long!(u8);
int_fits_c_long!(i16);
int_fits_c_long!(u16);
int_fits_c_long!(i32);

// If c_long is 64-bits, we can use more types with int_fits_c_long!:
#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
int_fits_c_long!(u32);
#[cfg(any(target_pointer_width="32", target_os="windows"))]
int_fits_larger_int!(u32, u64);

#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
int_fits_c_long!(i64);
// TODO: manual implementation for i64 on systems with 32-bit long

// u64 has a manual implementation as it never fits into signed long

#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
int_fits_c_long!(isize);
#[cfg(any(target_pointer_width="32", target_os="windows"))]
int_fits_larger_int!(isize, i64);

int_fits_larger_int!(usize, u64);

fn err_if_invalid_value<'p, T: PartialEq, F: Fn() -> T>
   (obj: &PyObject<'p>, invalid_value: T, func: F) -> PyResult<'p, T> {
    let py = obj.python();
    let v = func();
    if v == invalid_value && PyErr::occurred(py) {
        Err(PyErr::fetch(py))
    } else {
        Ok(v)
    }
}

impl <'p> ToPyObject<'p> for u64 {
    #[cfg(feature="python27-sys")]
    type ObjectType = PyObject<'p>;

    #[cfg(feature="python3-sys")]
    type ObjectType = PyLong<'p>;

    #[cfg(feature="python27-sys")]
    fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
        unsafe {
            let ptr = match num::traits::cast::<u64, c_long>(*self) {
                Some(v) => ffi::PyInt_FromLong(v),
                None => ffi::PyLong_FromUnsignedLongLong(*self)
            };
            err::from_owned_ptr_or_panic(py, ptr)
        }
    }

    #[cfg(feature="python3-sys")]
    fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyLong_FromUnsignedLongLong(*self))
        }
    }
}

impl <'p> FromPyObject<'p> for u64 {
    #[cfg(feature="python27-sys")]
    fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, u64> {
        let py = s.python();
        let ptr = s.as_ptr();

        unsafe {
            if ffi::PyLong_Check(ptr) != 0 {
                err_if_invalid_value(s, !0, || ffi::PyLong_AsUnsignedLongLong(s.as_ptr()) )
            } else if ffi::PyInt_Check(ptr) != 0 {
                match num::traits::cast::<c_long, u64>(ffi::PyInt_AS_LONG(ptr)) {
                    Some(v) => Ok(v),
                    None => Err(overflow_error(py))
                }
            } else {
                let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
                err_if_invalid_value(&num, !0, || ffi::PyLong_AsUnsignedLongLong(num.as_ptr()) )
            }
        }
    }

    #[cfg(feature="python3-sys")]
    fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, u64> {
        let py = s.python();
        let ptr = s.as_ptr();
        unsafe {
            if ffi::PyLong_Check(ptr) != 0 {
                err_if_invalid_value(s, !0, || ffi::PyLong_AsUnsignedLongLong(s.as_ptr()) )
            } else {
                let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
                err_if_invalid_value(&num, !0, || ffi::PyLong_AsUnsignedLongLong(num.as_ptr()) )
            }
        }
    }
}

impl <'p> ToPyObject<'p> for f64 {
    type ObjectType = PyFloat<'p>;

    fn to_py_object(&self, py: Python<'p>) -> PyFloat<'p> {
       PyFloat::new(py, *self)
    }
}

impl <'p> FromPyObject<'p> for f64 {
    fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, f64> {
        let py = s.python();
        let v = unsafe { ffi::PyFloat_AsDouble(s.as_ptr()) };
        if v == -1.0 && PyErr::occurred(py) {
            Err(PyErr::fetch(py))
        } else {
            Ok(v)
        }
    }
}

fn overflow_error(py: Python) -> PyErr {
    PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None)
}

impl <'p> ToPyObject<'p> for f32 {
    type ObjectType = PyFloat<'p>;

    fn to_py_object(&self, py: Python<'p>) -> PyFloat<'p> {
       PyFloat::new(py, *self as f64)
    }
}

impl <'p, 's> FromPyObject<'p> for f32 {
    fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, f32> {
        Ok(try!(s.extract::<f64>()) as f32)
    }
}

#[cfg(test)]
mod test {
    use std;
    use python::{Python, PythonObject};
    use conversion::ToPyObject;

    macro_rules! num_to_py_object_and_back (
        ($func_name:ident, $t1:ty, $t2:ty) => (
            #[test]
            fn $func_name() {
                let gil = Python::acquire_gil();
                let py = gil.python();
                let val = 123 as $t1;
                let obj = val.to_py_object(py).into_object();
                assert_eq!(obj.extract::<$t2>().unwrap(), val as $t2);
            }
        )
    );

    num_to_py_object_and_back!(to_from_f64, f64, f64);
    num_to_py_object_and_back!(to_from_f32, f32, f32);
    num_to_py_object_and_back!(to_from_i8,   i8,  i8);
    num_to_py_object_and_back!(to_from_u8,   u8,  u8);
    num_to_py_object_and_back!(to_from_i16, i16, i16);
    num_to_py_object_and_back!(to_from_u16, u16, u16);
    num_to_py_object_and_back!(to_from_i32, i32, i32);
    num_to_py_object_and_back!(to_from_u32, u32, u32);
    num_to_py_object_and_back!(to_from_i64, i64, i64);
    num_to_py_object_and_back!(to_from_u64, u64, u64);
    num_to_py_object_and_back!(to_from_isize, isize, isize);
    num_to_py_object_and_back!(to_from_usize, usize, usize);
    num_to_py_object_and_back!(float_to_i32, f64, i32);
    num_to_py_object_and_back!(float_to_u32, f64, u32);
    num_to_py_object_and_back!(float_to_i64, f64, i64);
    num_to_py_object_and_back!(float_to_u64, f64, u64);
    num_to_py_object_and_back!(int_to_float, i32, f64);

    #[test]
    fn test_u32_max() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = std::u32::MAX;
        let obj = v.to_py_object(py).into_object();
        assert_eq!(v, obj.extract::<u32>().unwrap());
        assert_eq!(v as u64, obj.extract::<u64>().unwrap());
        assert!(obj.extract::<i32>().is_err());
    }

    #[test]
    fn test_u64_max() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = std::u64::MAX;
        let obj = v.to_py_object(py).into_object();
        println!("{:?}", obj);
        assert_eq!(v, obj.extract::<u64>().unwrap());
        assert!(obj.extract::<i64>().is_err());
    }
}
<commit_msg>Converting function for i64 on 32bit/non-windows platform.<commit_after>// Copyright (c) 2015 Daniel Grunwald
//
// 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.

extern crate num;

use libc::{c_long, c_double};
use std;
use python::{Python, PythonObject, ToPythonPointer};
use err::{self, PyResult, PyErr};
use super::object::PyObject;
use super::exc;
use ffi::{self, Py_ssize_t};
use conversion::{ToPyObject, FromPyObject};

#[cfg(feature="python27-sys")]
pyobject_newtype!(PyInt, PyInt_Check, PyInt_Type);

pyobject_newtype!(PyLong, PyLong_Check, PyLong_Type);
pyobject_newtype!(PyFloat, PyFloat_Check, PyFloat_Type);

#[cfg(feature="python27-sys")]
impl <'p> PyInt<'p> {
    /// Creates a new python `int` object.
    pub fn new(py: Python<'p>, val: c_long) -> PyInt<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyInt_FromLong(val))
        }
    }

    /// Gets the value of this integer.
    pub fn value(&self) -> c_long {
        unsafe { ffi::PyInt_AS_LONG(self.as_ptr()) }
    }
}

impl <'p> PyFloat<'p> {
    /// Creates a new python `float` object.
    pub fn new(py: Python<'p>, val: c_double) -> PyFloat<'p> {
        unsafe {
            err::cast_from_owned_ptr_or_panic(py, ffi::PyFloat_FromDouble(val))
        }
    }

    /// Gets the value of this float.
    pub fn value(&self) -> c_double {
        unsafe { ffi::PyFloat_AsDouble(self.as_ptr()) }
    }
}

macro_rules! int_fits_c_long(
    ($rust_type:ty) => (
        #[cfg(feature="python27-sys")]
        impl <'p> ToPyObject<'p> for $rust_type {
            type ObjectType = PyInt<'p>;

            fn to_py_object(&self, py: Python<'p>) -> PyInt<'p> {
                unsafe {
                    err::cast_from_owned_ptr_or_panic(py,
                        ffi::PyInt_FromLong(*self as c_long))
                }
            }
        }

        #[cfg(feature="python3-sys")]
        impl <'p> ToPyObject<'p> for $rust_type {
            type ObjectType = PyLong<'p>;

            fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
                unsafe {
                    err::cast_from_owned_ptr_or_panic(py,
                        ffi::PyLong_FromLong(*self as c_long))
                }
            }
        }

        #[cfg(feature="python27-sys")]
        impl <'p> FromPyObject<'p> for $rust_type {
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let val = unsafe { ffi::PyInt_AsLong(s.as_ptr()) };
                if val == -1 && PyErr::occurred(py) {
                    return Err(PyErr::fetch(py));
                }
                match num::traits::cast::<c_long, $rust_type>(val) {
                    Some(v) => Ok(v),
                    None => Err(overflow_error(py))
                }
            }
        }

        #[cfg(feature="python3-sys")]
        impl <'p> FromPyObject<'p> for $rust_type {
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let val = unsafe { ffi::PyLong_AsLong(s.as_ptr()) };
                if val == -1 && PyErr::occurred(py) {
                    return Err(PyErr::fetch(py));
                }
                match num::traits::cast::<c_long, $rust_type>(val) {
                    Some(v) => Ok(v),
                    None => Err(overflow_error(py))
                }
            }
        }
    )
);

macro_rules! int_fits_larger_int(
    ($rust_type:ty, $larger_type:ty) => (
        impl <'p> ToPyObject<'p> for $rust_type {
            type ObjectType = <$larger_type as ToPyObject<'p>>::ObjectType;

            #[inline]
            fn to_py_object(&self, py: Python<'p>) -> <$larger_type as ToPyObject<'p>>::ObjectType {
                (*self as $larger_type).to_py_object(py)
            }
        }

        impl <'p> FromPyObject<'p> for $rust_type {
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let val = try!(s.extract::<$larger_type>());
                match num::traits::cast::<$larger_type, $rust_type>(val) {
                    Some(v) => Ok(v),
                    None => Err(overflow_error(py))
                }
            }
        }
    )
);

fn err_if_invalid_value<'p, T: PartialEq, F: Fn() -> T>
   (obj: &PyObject<'p>, invalid_value: T, func: F) -> PyResult<'p, T> {
    let py = obj.python();
    let v = func();
    if v == invalid_value && PyErr::occurred(py) {
        Err(PyErr::fetch(py))
    } else {
        Ok(v)
    }
}

macro_rules! int_convert_u64_or_i64 (
    ($rust_type:ty, $pylong_from_ll_or_ull:expr, $pylong_as_ull_or_ull:expr) => (
        impl <'p> ToPyObject<'p> for $rust_type {
            #[cfg(feature="python27-sys")]
            type ObjectType = PyObject<'p>;

            #[cfg(feature="python3-sys")]
            type ObjectType = PyLong<'p>;

            #[cfg(feature="python27-sys")]
            fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
                unsafe {
                    let ptr = match num::traits::cast::<$rust_type, c_long>(*self) {
                        Some(v) => ffi::PyInt_FromLong(v),
                        None => $pylong_from_ll_or_ull(*self)
                    };
                    err::from_owned_ptr_or_panic(py, ptr)
                }
            }

            #[cfg(feature="python3-sys")]
            fn to_py_object(&self, py: Python<'p>) -> PyLong<'p> {
                unsafe {
                    err::cast_from_owned_ptr_or_panic(py, $pylong_from_ll_or_ull(*self))
                }
            }
        }

        impl <'p> FromPyObject<'p> for $rust_type {
            #[cfg(feature="python27-sys")]
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let ptr = s.as_ptr();

                unsafe {
                    if ffi::PyLong_Check(ptr) != 0 {
                        err_if_invalid_value(s, !0, || $pylong_as_ull_or_ull(s.as_ptr()) )
                    } else if ffi::PyInt_Check(ptr) != 0 {
                        match num::traits::cast::<c_long, $rust_type>(ffi::PyInt_AS_LONG(ptr)) {
                            Some(v) => Ok(v),
                            None => Err(overflow_error(py))
                        }
                    } else {
                        let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
                        err_if_invalid_value(&num, !0, || $pylong_as_ull_or_ull(num.as_ptr()) )
                    }
                }
            }

            #[cfg(feature="python3-sys")]
            fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, $rust_type> {
                let py = s.python();
                let ptr = s.as_ptr();
                unsafe {
                    if ffi::PyLong_Check(ptr) != 0 {
                        err_if_invalid_value(s, !0, || $pylong_as_ull_or_ull(s.as_ptr()) )
                    } else {
                        let num = try!(err::result_from_owned_ptr(py, ffi::PyNumber_Long(ptr)));
                        err_if_invalid_value(&num, !0, || $pylong_as_ull_or_ull(num.as_ptr()) )
                    }
                }
            }
        }
    )
);

int_fits_c_long!(i8);
int_fits_c_long!(u8);
int_fits_c_long!(i16);
int_fits_c_long!(u16);
int_fits_c_long!(i32);

// If c_long is 64-bits, we can use more types with int_fits_c_long!:
#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
int_fits_c_long!(u32);
#[cfg(any(target_pointer_width="32", target_os="windows"))]
int_fits_larger_int!(u32, u64);

#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
int_fits_c_long!(i64);

// manual implementation for i64 on systems with 32-bit long
#[cfg(all(target_pointer_width="32", not(target_os="windows")))]
int_convert_u64_or_i64!(i64, ffi::PyLong_FromLongLong, ffi::PyLong_AsLongLong);

#[cfg(all(target_pointer_width="64", not(target_os="windows")))]
int_fits_c_long!(isize);
#[cfg(any(target_pointer_width="32", target_os="windows"))]
int_fits_larger_int!(isize, i64);

int_fits_larger_int!(usize, u64);

// u64 has a manual implementation as it never fits into signed long
int_convert_u64_or_i64!(u64, ffi::PyLong_FromUnsignedLongLong, ffi::PyLong_AsUnsignedLongLong);

impl <'p> ToPyObject<'p> for f64 {
    type ObjectType = PyFloat<'p>;

    fn to_py_object(&self, py: Python<'p>) -> PyFloat<'p> {
       PyFloat::new(py, *self)
    }
}

impl <'p> FromPyObject<'p> for f64 {
    fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, f64> {
        let py = s.python();
        let v = unsafe { ffi::PyFloat_AsDouble(s.as_ptr()) };
        if v == -1.0 && PyErr::occurred(py) {
            Err(PyErr::fetch(py))
        } else {
            Ok(v)
        }
    }
}

fn overflow_error(py: Python) -> PyErr {
    PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None)
}

impl <'p> ToPyObject<'p> for f32 {
    type ObjectType = PyFloat<'p>;

    fn to_py_object(&self, py: Python<'p>) -> PyFloat<'p> {
       PyFloat::new(py, *self as f64)
    }
}

impl <'p, 's> FromPyObject<'p> for f32 {
    fn from_py_object(s: &PyObject<'p>) -> PyResult<'p, f32> {
        Ok(try!(s.extract::<f64>()) as f32)
    }
}

#[cfg(test)]
mod test {
    use std;
    use python::{Python, PythonObject};
    use conversion::ToPyObject;

    macro_rules! num_to_py_object_and_back (
        ($func_name:ident, $t1:ty, $t2:ty) => (
            #[test]
            fn $func_name() {
                let gil = Python::acquire_gil();
                let py = gil.python();
                let val = 123 as $t1;
                let obj = val.to_py_object(py).into_object();
                assert_eq!(obj.extract::<$t2>().unwrap(), val as $t2);
            }
        )
    );

    num_to_py_object_and_back!(to_from_f64, f64, f64);
    num_to_py_object_and_back!(to_from_f32, f32, f32);
    num_to_py_object_and_back!(to_from_i8,   i8,  i8);
    num_to_py_object_and_back!(to_from_u8,   u8,  u8);
    num_to_py_object_and_back!(to_from_i16, i16, i16);
    num_to_py_object_and_back!(to_from_u16, u16, u16);
    num_to_py_object_and_back!(to_from_i32, i32, i32);
    num_to_py_object_and_back!(to_from_u32, u32, u32);
    num_to_py_object_and_back!(to_from_i64, i64, i64);
    num_to_py_object_and_back!(to_from_u64, u64, u64);
    num_to_py_object_and_back!(to_from_isize, isize, isize);
    num_to_py_object_and_back!(to_from_usize, usize, usize);
    num_to_py_object_and_back!(float_to_i32, f64, i32);
    num_to_py_object_and_back!(float_to_u32, f64, u32);
    num_to_py_object_and_back!(float_to_i64, f64, i64);
    num_to_py_object_and_back!(float_to_u64, f64, u64);
    num_to_py_object_and_back!(int_to_float, i32, f64);

    #[test]
    fn test_u32_max() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = std::u32::MAX;
        let obj = v.to_py_object(py).into_object();
        assert_eq!(v, obj.extract::<u32>().unwrap());
        assert_eq!(v as u64, obj.extract::<u64>().unwrap());
        assert!(obj.extract::<i32>().is_err());
    }

    #[test]
    fn test_u64_max() {
        let gil = Python::acquire_gil();
        let py = gil.python();
        let v = std::u64::MAX;
        let obj = v.to_py_object(py).into_object();
        println!("{:?}", obj);
        assert_eq!(v, obj.extract::<u64>().unwrap());
        assert!(obj.extract::<i64>().is_err());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    admission_control_service::{
        AdmissionControlService, SubmitTransactionRequest,
        SubmitTransactionResponse as ProtoSubmitTransactionResponse,
    },
    mocks::local_mock_mempool::LocalMockMempool,
};
use admission_control_proto::{AdmissionControlStatus, SubmitTransactionResponse};

use crypto::{ed25519::*, test_utils::TEST_SEED};
use mempool::proto::shared::mempool_status::MempoolAddTransactionStatusCode;
use proto_conv::FromProto;
use rand::SeedableRng;
use std::sync::Arc;
use storage_service::mocks::mock_storage_client::MockStorageReadClient;
use types::{
    account_address::{AccountAddress, ADDRESS_LENGTH},
    test_helpers::transaction_test_helpers::get_test_signed_txn,
    vm_error::{StatusCode, VMStatus},
};
use vm_validator::mocks::mock_vm_validator::MockVMValidator;

pub fn create_ac_service_for_ut() -> AdmissionControlService<LocalMockMempool, MockVMValidator> {
    AdmissionControlService::new(
        Some(Arc::new(LocalMockMempool::new())),
        Arc::new(MockStorageReadClient),
        Arc::new(MockVMValidator),
        false,
    )
}

fn assert_status(response: ProtoSubmitTransactionResponse, status: VMStatus) {
    let rust_resp = SubmitTransactionResponse::from_proto(response).unwrap();
    if let Some(resp_ac_status) = rust_resp.ac_status {
        assert_eq!(resp_ac_status, AdmissionControlStatus::Accepted);
    } else {
        let decoded_response = rust_resp.vm_error.unwrap();
        assert_eq!(decoded_response.major_status, status.major_status);
        assert_eq!(decoded_response.sub_status, status.sub_status);
    }
}

#[test]
fn test_submit_txn_inner_vm() {
    let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED);
    let ac_service = create_ac_service_for_ut();
    // create request
    let mut req: SubmitTransactionRequest = SubmitTransactionRequest::new();
    let sender = AccountAddress::new([0; ADDRESS_LENGTH]);
    let keypair = compat::generate_keypair(&mut rng);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(
        response,
        VMStatus::new(StatusCode::SENDING_ACCOUNT_DOES_NOT_EXIST),
    );
    let sender = AccountAddress::new([1; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::INVALID_SIGNATURE));
    let sender = AccountAddress::new([2; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(
        response,
        VMStatus::new(StatusCode::INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE),
    );
    let sender = AccountAddress::new([3; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::SEQUENCE_NUMBER_TOO_NEW));
    let sender = AccountAddress::new([4; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::SEQUENCE_NUMBER_TOO_OLD));
    let sender = AccountAddress::new([5; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::TRANSACTION_EXPIRED));
    let sender = AccountAddress::new([6; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::INVALID_AUTH_KEY));
    let sender = AccountAddress::new([8; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::EXECUTED));

    let sender = AccountAddress::new([8; ADDRESS_LENGTH]);
    let test_key = compat::generate_keypair(&mut rng);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        test_key.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::INVALID_SIGNATURE));
}

#[test]
fn test_submit_txn_inner_mempool() {
    let ac_service = create_ac_service_for_ut();
    let mut req: SubmitTransactionRequest = SubmitTransactionRequest::new();
    let keypair = compat::generate_keypair(None);
    let insufficient_balance_add = AccountAddress::new([100; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        insufficient_balance_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.mempool_error.unwrap().code,
        MempoolAddTransactionStatusCode::InsufficientBalance
    );
    let invalid_seq_add = AccountAddress::new([101; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        invalid_seq_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.mempool_error.unwrap().code,
        MempoolAddTransactionStatusCode::InvalidSeqNumber
    );
    let sys_error_add = AccountAddress::new([102; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sys_error_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.mempool_error.unwrap().code,
        MempoolAddTransactionStatusCode::InvalidUpdate
    );
    let accepted_add = AccountAddress::new([103; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        accepted_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.ac_status.unwrap(),
        AdmissionControlStatus::Accepted,
    );
}
<commit_msg> [admission_control tests] add test missing error case `MempoolIsFull`<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    admission_control_service::{
        AdmissionControlService, SubmitTransactionRequest,
        SubmitTransactionResponse as ProtoSubmitTransactionResponse,
    },
    mocks::local_mock_mempool::LocalMockMempool,
};
use admission_control_proto::{AdmissionControlStatus, SubmitTransactionResponse};

use crypto::{ed25519::*, test_utils::TEST_SEED};
use mempool::proto::shared::mempool_status::MempoolAddTransactionStatusCode;
use proto_conv::FromProto;
use rand::SeedableRng;
use std::sync::Arc;
use storage_service::mocks::mock_storage_client::MockStorageReadClient;
use types::{
    account_address::{AccountAddress, ADDRESS_LENGTH},
    test_helpers::transaction_test_helpers::get_test_signed_txn,
    vm_error::{StatusCode, VMStatus},
};
use vm_validator::mocks::mock_vm_validator::MockVMValidator;

pub fn create_ac_service_for_ut() -> AdmissionControlService<LocalMockMempool, MockVMValidator> {
    AdmissionControlService::new(
        Some(Arc::new(LocalMockMempool::new())),
        Arc::new(MockStorageReadClient),
        Arc::new(MockVMValidator),
        false,
    )
}

fn assert_status(response: ProtoSubmitTransactionResponse, status: VMStatus) {
    let rust_resp = SubmitTransactionResponse::from_proto(response).unwrap();
    if let Some(resp_ac_status) = rust_resp.ac_status {
        assert_eq!(resp_ac_status, AdmissionControlStatus::Accepted);
    } else {
        let decoded_response = rust_resp.vm_error.unwrap();
        assert_eq!(decoded_response.major_status, status.major_status);
        assert_eq!(decoded_response.sub_status, status.sub_status);
    }
}

#[test]
fn test_submit_txn_inner_vm() {
    let mut rng = ::rand::rngs::StdRng::from_seed(TEST_SEED);
    let ac_service = create_ac_service_for_ut();
    // create request
    let mut req: SubmitTransactionRequest = SubmitTransactionRequest::new();
    let sender = AccountAddress::new([0; ADDRESS_LENGTH]);
    let keypair = compat::generate_keypair(&mut rng);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(
        response,
        VMStatus::new(StatusCode::SENDING_ACCOUNT_DOES_NOT_EXIST),
    );
    let sender = AccountAddress::new([1; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::INVALID_SIGNATURE));
    let sender = AccountAddress::new([2; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(
        response,
        VMStatus::new(StatusCode::INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE),
    );
    let sender = AccountAddress::new([3; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::SEQUENCE_NUMBER_TOO_NEW));
    let sender = AccountAddress::new([4; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::SEQUENCE_NUMBER_TOO_OLD));
    let sender = AccountAddress::new([5; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::TRANSACTION_EXPIRED));
    let sender = AccountAddress::new([6; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::INVALID_AUTH_KEY));
    let sender = AccountAddress::new([8; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::EXECUTED));

    let sender = AccountAddress::new([8; ADDRESS_LENGTH]);
    let test_key = compat::generate_keypair(&mut rng);
    req.set_signed_txn(get_test_signed_txn(
        sender,
        0,
        keypair.0.clone(),
        test_key.1.clone(),
        None,
    ));
    let response = ac_service.submit_transaction_inner(req.clone()).unwrap();
    assert_status(response, VMStatus::new(StatusCode::INVALID_SIGNATURE));
}

#[test]
fn test_submit_txn_inner_mempool() {
    let ac_service = create_ac_service_for_ut();
    let mut req: SubmitTransactionRequest = SubmitTransactionRequest::new();
    let keypair = compat::generate_keypair(None);
    let insufficient_balance_add = AccountAddress::new([100; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        insufficient_balance_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.mempool_error.unwrap().code,
        MempoolAddTransactionStatusCode::InsufficientBalance
    );
    let invalid_seq_add = AccountAddress::new([101; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        invalid_seq_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.mempool_error.unwrap().code,
        MempoolAddTransactionStatusCode::InvalidSeqNumber
    );
    let sys_error_add = AccountAddress::new([102; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        sys_error_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.mempool_error.unwrap().code,
        MempoolAddTransactionStatusCode::InvalidUpdate
    );
    let accepted_add = AccountAddress::new([103; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        accepted_add,
        0,
        keypair.0.clone(),
        keypair.1.clone(),
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.ac_status.unwrap(),
        AdmissionControlStatus::Accepted,
    );
    let accepted_add = AccountAddress::new([104; ADDRESS_LENGTH]);
    req.set_signed_txn(get_test_signed_txn(
        accepted_add,
        0,
        keypair.0.clone(),
        keypair.1,
        None,
    ));
    let response = SubmitTransactionResponse::from_proto(
        ac_service.submit_transaction_inner(req.clone()).unwrap(),
    )
    .unwrap();
    assert_eq!(
        response.mempool_error.unwrap().code,
        MempoolAddTransactionStatusCode::MempoolIsFull,
    );
}
<|endoftext|>


<commit_before>use ast::AST;
use ast::atom_quote;

#[deriving(Show, PartialEq)]
pub enum Error {
    UnboundVariable(String),
    UnappliableValue(AST),
    WrongArgumentType(AST),
    BadArity(String),
}

pub fn eval(value: AST) -> Result<AST, Error> {
    match value {
        AST::Atom(atom) =>
            Err(Error::UnboundVariable(atom)),
        AST::Bool(_b) =>
            Ok(value),
        AST::Integer(_i) =>
            Ok(value),
        AST::List(ref list) =>
            eval_list(list.as_slice()),
    }
}

fn eval_list(list: &[AST]) -> Result<AST, Error> {
    if list.is_empty() {
        return Ok(AST::List(vec!()));
    }

    let fun = list.head().unwrap();
    let args = list.tail();

    match fun {
        &AST::Atom(ref atom) =>
            eval_fun(atom.as_slice(), args),
        _ =>
            Err(Error::UnappliableValue(fun.clone()))
    }
}

fn eval_fun(name: &str, args: &[AST]) -> Result<AST, Error> {
    match name {
        "quote" => eval_fun_quote(args),
        "+"     => eval_fun_plus(args),
        "-"     => eval_fun_minus(args),
        _       => Err(Error::UnboundVariable(name.to_string()))
    }
}

fn eval_fun_plus(args: &[AST]) -> Result<AST, Error> {
    let mut sum: i64 = 0;

    for val in args.iter() {
        let evald_val = try!(eval(val.clone()));

        match evald_val {
            AST::Integer(n) =>
                sum = sum + n,
            _ =>
                return Err(Error::WrongArgumentType(evald_val))
        };
    }

    Ok(AST::Integer(sum))
}

fn eval_fun_minus(args: &[AST]) -> Result<AST, Error> {
    if args.len() == 0 {
        Err(Error::BadArity("-".to_string()))
    } else {
        let head = args.head().unwrap();
        let tail = args.tail();

        let evald_head = try!(eval(head.clone()));

        let start = match evald_head {
            AST::Integer(n) => n,
            _               => return Err(Error::WrongArgumentType(evald_head))
        };

        if tail.is_empty() {
            Ok(AST::Integer(-start))
        } else {
            let mut sum: i64 = 0;

            for val in tail.iter() {
                let evald_val = try!(eval(val.clone()));

                match evald_val {
                    AST::Integer(n) =>
                        sum = sum + n,
                    _ =>
                        return Err(Error::WrongArgumentType(evald_val))
                };
            }

            Ok(AST::Integer(start - sum))
        }
    }
}

fn eval_fun_quote(args: &[AST]) -> Result<AST, Error> {
    Ok(args[0].clone())
}
<commit_msg>Removed unused import of ast::atom_quote.<commit_after>use ast::AST;

#[deriving(Show, PartialEq)]
pub enum Error {
    UnboundVariable(String),
    UnappliableValue(AST),
    WrongArgumentType(AST),
    BadArity(String),
}

pub fn eval(value: AST) -> Result<AST, Error> {
    match value {
        AST::Atom(atom) =>
            Err(Error::UnboundVariable(atom)),
        AST::Bool(_b) =>
            Ok(value),
        AST::Integer(_i) =>
            Ok(value),
        AST::List(ref list) =>
            eval_list(list.as_slice()),
    }
}

fn eval_list(list: &[AST]) -> Result<AST, Error> {
    if list.is_empty() {
        return Ok(AST::List(vec!()));
    }

    let fun = list.head().unwrap();
    let args = list.tail();

    match fun {
        &AST::Atom(ref atom) =>
            eval_fun(atom.as_slice(), args),
        _ =>
            Err(Error::UnappliableValue(fun.clone()))
    }
}

fn eval_fun(name: &str, args: &[AST]) -> Result<AST, Error> {
    match name {
        "quote" => eval_fun_quote(args),
        "+"     => eval_fun_plus(args),
        "-"     => eval_fun_minus(args),
        _       => Err(Error::UnboundVariable(name.to_string()))
    }
}

fn eval_fun_plus(args: &[AST]) -> Result<AST, Error> {
    let mut sum: i64 = 0;

    for val in args.iter() {
        let evald_val = try!(eval(val.clone()));

        match evald_val {
            AST::Integer(n) =>
                sum = sum + n,
            _ =>
                return Err(Error::WrongArgumentType(evald_val))
        };
    }

    Ok(AST::Integer(sum))
}

fn eval_fun_minus(args: &[AST]) -> Result<AST, Error> {
    if args.len() == 0 {
        Err(Error::BadArity("-".to_string()))
    } else {
        let head = args.head().unwrap();
        let tail = args.tail();

        let evald_head = try!(eval(head.clone()));

        let start = match evald_head {
            AST::Integer(n) => n,
            _               => return Err(Error::WrongArgumentType(evald_head))
        };

        if tail.is_empty() {
            Ok(AST::Integer(-start))
        } else {
            let mut sum: i64 = 0;

            for val in tail.iter() {
                let evald_val = try!(eval(val.clone()));

                match evald_val {
                    AST::Integer(n) =>
                        sum = sum + n,
                    _ =>
                        return Err(Error::WrongArgumentType(evald_val))
                };
            }

            Ok(AST::Integer(start - sum))
        }
    }
}

fn eval_fun_quote(args: &[AST]) -> Result<AST, Error> {
    Ok(args[0].clone())
}
<|endoftext|>